Как игнорирование потока диспетчеризации событий позволяет этой программе работать?
Как я пытался понять, смогу ли я ответить на этот вопрос сегодня утром. Я понял, что я не совсем понимаю 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, чтобы сделать что-то вроде того, что вы делаете в цикле.