Java 为什么SwingUtilities.invokeLater()会导致JButton冻结?
考虑这个基本的Swing程序,它由两个按钮组成:Java 为什么SwingUtilities.invokeLater()会导致JButton冻结?,java,multithreading,swing,Java,Multithreading,Swing,考虑这个基本的Swing程序,它由两个按钮组成: public class main { public static void main(String[] args) { JFrame jf = new JFrame("hi!"); JPanel mainPanel = new JPanel(new GridLayout()); JButton longAction = new JButton("long action");
public class main {
public static void main(String[] args) {
JFrame jf = new JFrame("hi!");
JPanel mainPanel = new JPanel(new GridLayout());
JButton longAction = new JButton("long action");
longAction.addActionListener(event -> doLongAction());
JButton testSystemOut = new JButton("test System.out");
testSystemOut.addActionListener(event -> System.out.println("this is a test"));
mainPanel.add(longAction);
mainPanel.add(testSystemOut);
jf.add(mainPanel);
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
public static void doLongAction() {
SwingUtilities.invokeLater(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("Interrupted!");
}
System.out.println("Finished long action");
});
}
}
我希望我的第二个按钮testSystemOut可以使用,而第一个按钮正在执行它的长操作,我在其中放置了3秒钟的睡眠。我可以通过在线程中手动放置doLongAction并调用start来实现这一点。但我已经读到我应该使用SwingUtilities,它的工作原理与这里的EventQueue完全相同。但是,如果我这样做,我的按钮将在其操作期间冻结
为什么?通过使用SwingUtilities.invokeLater,您正在调用附带的代码,包括Thread.sleep。。。调用Swing事件线程,这是您永远不应该做的事情,因为它将整个事件线程(负责绘制GUI并响应用户输入的线程)置于睡眠状态,即冻结应用程序。解决方案:改用一个线程,或者在后台线程中睡觉。如果您正在调用长时间运行的代码并使用Thread.sleep。。。要模拟它,请使用SwingWorker为您做背景工作。请阅读以了解这方面的详细信息。请注意,没有理由使用SwingUtilities.invokeLater,因为不管Swing事件线程如何,ActionListener代码都将在EDT上调用。但是,我会使用SwingUtilities.invokeLater来创建GUI
e、 g
通过使用SwingUtilities.invokeLater,您正在调用包含的代码,包括Thread.sleep。。。调用Swing事件线程,这是您永远不应该做的事情,因为它将整个事件线程(负责绘制GUI并响应用户输入的线程)置于睡眠状态,即冻结应用程序。解决方案:改用一个线程,或者在后台线程中睡觉。如果您正在调用长时间运行的代码并使用Thread.sleep。。。要模拟它,请使用SwingWorker为您做背景工作。请阅读以了解这方面的详细信息。请注意,没有理由使用SwingUtilities.invokeLater,因为不管Swing事件线程如何,ActionListener代码都将在EDT上调用。但是,我会使用SwingUtilities.invokeLater来创建GUI
e、 g
不要使用SwingUtilities。调用器。。。当您想要执行一些长时间运行的代码时。在单独的普通线程中执行此操作
Swing不是多线程的,它是事件驱动的。因此,有一些方法,如SwingUtilities.invokeLater。。。。如果您想从不同的线程更改Swing组件,则必须使用这些方法,因为Swing不是线程安全的,例如,如果您想更改按钮的文本。
与GUI相关的所有内容都在该Swing线程中运行,例如光标闪烁、来自操作系统的消息、用户命令等。
由于它是一个线程,因此该线程中的每一个长时间运行的代码都会阻塞您的GUI
如果您只是执行一些与GUI无关的长时间运行的代码,那么它不应该在Swing事件线程中运行,而是在它自己的独立线程中运行
看
解释为什么Swing不是多线程的。不要使用SwingUtilities.invokeLater。。。当您想要执行一些长时间运行的代码时。在单独的普通线程中执行此操作
Swing不是多线程的,它是事件驱动的。因此,有一些方法,如SwingUtilities.invokeLater。。。。如果您想从不同的线程更改Swing组件,则必须使用这些方法,因为Swing不是线程安全的,例如,如果您想更改按钮的文本。
与GUI相关的所有内容都在该Swing线程中运行,例如光标闪烁、来自操作系统的消息、用户命令等。
由于它是一个线程,因此该线程中的每一个长时间运行的代码都会阻塞您的GUI
如果您只是执行一些与GUI无关的长时间运行的代码,那么它不应该在Swing事件线程中运行,而是在它自己的独立线程中运行
看
为什么Swing不是多线程的。谢谢,我理解。我不应该简单地使用addActionListenerevent->new Thread->doLongAction.start,有什么原因吗;在我的longAction按钮上自定义线程,而不是定义我自己的SwingWorker?说到长工,不是吗TimerAction@Blauhirn:这也会起作用,但使用SwingWorker有几个优点,包括它的发布/处理方法对,可以轻松地将数据从后台线程传递到事件线程上的GUI,例如SwingWorker progress属性,使其易于在JProgressBar中显示SwingWorker的工作进度,例如它易于与PropertyChangeListener一起使用,使其易于在SwingWorker完成任务时获得通知。如果需要,SwingWorker还可以在完成后返回值。谢谢,我理解。我不应该简单地使用addActionListenerevent->new Thread->doLongAction.start,有什么原因吗;在我的longAction按钮上自定义线程,而不是定义我自己的SwingWorker?说到长工,不是吗TimerAction@Blauhirn:那也行,但有几个 通过使用SwingWorker获得的所有优势,包括它的发布/处理方法对,可以轻松地将数据从后台线程传递到事件线程上的GUI,例如SwingWorker progress属性,可以轻松地在JProgressBar中显示SwingWorker的工作进度,例如,它易于与PropertyChangeListener一起使用,使得SwingWorker完成任务后可以很容易地得到通知。如果需要,SwingWorker还可以在完成后返回一个值。
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame jf = new JFrame("hi!");
JPanel mainPanel = new JPanel(new GridLayout());
JButton testSystemOut = new JButton("test System.out");
testSystemOut.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("this is a test");
}
});
mainPanel.add(new JButton(new LongAction("Long Action")));
mainPanel.add(new JButton(new TimerAction("Timer Action")));
mainPanel.add(testSystemOut);
jf.add(mainPanel);
jf.pack();
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setVisible(true);
}
});
}
@SuppressWarnings("serial")
public static class LongAction extends AbstractAction {
private LongWorker longWorker = null;
public LongAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
@Override
public void actionPerformed(ActionEvent e) {
setEnabled(false);
longWorker = new LongWorker(); // create a new SwingWorker
// add listener to respond to completion of the worker's work
longWorker.addPropertyChangeListener(new LongWorkerListener(this));
// run the worker
longWorker.execute();
}
}
public static class LongWorker extends SwingWorker<Void, Void> {
private static final long SLEEP_TIME = 3 * 1000;
@Override
protected Void doInBackground() throws Exception {
Thread.sleep(SLEEP_TIME);
System.out.println("Finished with long action!");
return null;
}
}
public static class LongWorkerListener implements PropertyChangeListener {
private LongAction longAction;
public LongWorkerListener(LongAction longAction) {
this.longAction = longAction;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
// if the worker is done, re-enable the Action and thus the JButton
longAction.setEnabled(true);
LongWorker worker = (LongWorker) evt.getSource();
try {
// call get to trap any exceptions that might have happened during worker's run
worker.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
@SuppressWarnings("serial")
public static class TimerAction extends AbstractAction {
private static final int TIMER_DELAY = 3 * 1000;
public TimerAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
@Override
public void actionPerformed(ActionEvent e) {
setEnabled(false);
new Timer(TIMER_DELAY, new TimerListener(this)).start();
}
}
public static class TimerListener implements ActionListener {
private TimerAction timerAction;
public TimerListener(TimerAction timerAction) {
this.timerAction = timerAction;
}
@Override
public void actionPerformed(ActionEvent e) {
timerAction.setEnabled(true);
System.out.println("Finished Timer Action!");
((Timer) e.getSource()).stop();
}
}
}