Java 为什么SwingUtilities.invokeLater()会导致JButton冻结?

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");

考虑这个基本的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");
        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();
      }
   }
}