Как игнорирование потока диспетчеризации событий позволяет этой программе работать?

Как я пытался понять, смогу ли я ответить на этот вопрос сегодня утром. Я понял, что я не совсем понимаю Event Dispatch Thread (EDT). Погуглил и подтвердил и помог с этим и объяснил почему я не делаю. ( Это также может иметь отношение к пониманию.)

Код устанавливает графический интерфейс, а затем (как и в предыдущем вопросе) обновляет текстовое поле, пока флаг не будет установлен.

У меня есть несколько вопросов / запросов.

  • Пожалуйста, объясните, почему код ниже работает нормально, если оба вызова swingInit а также doIt) находятся за пределами invokeLater блок (как показано), поскольку оба вызова влияют или запрашивают графический интерфейс, но ни один из них не выполняется в EDT (не так ли?). Разве это не провоцирует провал?

  • Код также работает, если вызов swingInit внутри и doIt вне invokeLater, Так swingInit выполняется на EDT, но не должен doIt не выполняется на EDT быть проблемой? (Я был удивлен, что это сработало. Должен ли я был?)

  • Я думаю, я понимаю, почему он зависает, если doIt это внутри invokeLater независимо от того, где swingInit это: цель invokeLater ТОЛЬКО для инициализации графического интерфейса (верно?).

  • Должен doIt быть инициированным (возможно, из события, происходящего) на EDT, но, конечно, не внутри invokeLater блок?

(История концепции EDT интересна. Так было не всегда. См. Ссылку выше на вопрос "почему я не понимаю".)

import static java.awt.EventQueue.invokeLater;
import java.awt.event.*;
import javax.swing.*;

public class Whatever 
{
  static boolean flag = true;
  static JTextField tf = new JTextField("Hi",20);
  static JPanel p = new JPanel();
  static JFrame f = new JFrame();
  static JButton b = new JButton("End");

  public static void main(String[] args)
  {
    swingInit();

    invokeLater
    (
      new Runnable()
      {
        @Override
        public void run() 
        {
    //    swingInit();
    //    doIt();
        }
      }
    ); 
   doIt();      
  } 

  static void swingInit()
  {
     b.addMouseListener
    (
        new MouseAdapter()
        {
          @Override
          public void mouseClicked(MouseEvent e)
          {
            flag = false;
            JOptionPane.showMessageDialog(null,"Clicked... exiting");
            System.exit(0);
          }
        }
    );

    p.add(tf);
    p.add(b);
    f.add(p);
    f.setVisible(true);
    f.pack();
    f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
  }

  static String getInfo(){
    return "Hello... " + Math.random();
  }

  static void doIt(){
    while(flag)     
      tf.setText(getInfo());
  }; 
}

2 ответа

Решение

Взяв каждый из ваших пунктов пули:

  • Код запускается в главном потоке - EDT запускается параллельно.swingInit возвращается после создания пользовательского интерфейса, который затем находится под контролем EDT, что позволяет dotIt делать свое дело параллельно в главном потоке

  • Ситуация аналогична описанной выше, но здесь вы гарантируете создание пользовательского интерфейса на EDT (в соответствии с рекомендациями Oracle).

  • Долгосрочное задание помещается в EDT, предотвращая его показ (если оно было установлено до swingIt) или живопись и взаимодействие (если размещено после). the purpose of invokeLater is ONLY to initialize the GUI Цель состоит в том, чтобы размещать не-потокобезопасные вызовы Swing на EDT. Если в рамках основного метода, я бы порекомендовал использовать SwingUtilities.invokeAndWait

  • Если вы хотите обновить подобный интерфейс, попробуйте сделать это с помощью SwingTimer,

Выполнение специального, не относящегося к потоку, кода, не относящегося к EDT, вне EDT не гарантирует сбой, но вызывает сбой (через конфликты, когда два (или более) потока пытаются обновить данные одновременно).

Однажды я часами выслеживал загадочного NullPointerExceptionтолько чтобы понять, что это был LookAndFeel вопрос, чьи звонки не были на EDT. Урок выучен.

static void doIt(){
  while(flag)     
    tf.setText(getInfo());
}; 

Эта занятая петля внутри doIt связывает поток GUI (вращающийся в этом цикле), что приведет к зависанию GUI.

Вы на самом деле не объяснили, что вы имеете в виду под "работает нормально", но я предполагаю, что это проблема, которую вы видите.

Возможно, вы захотите использовать таймер Swing, чтобы сделать что-то вроде того, что вы делаете в цикле.

Другие вопросы по тегам