java - 为什么 SwingUtilities.invokeLater() 会导致 JButton 卡住?

标签 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");
        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 在第一个按钮进行长时间操作时可用(在这里,我让它 hibernate 了 3 秒)。我可以通过手动将doLongAction() 放入Thread 并调用start() 来做到这一点。但我读过我应该改用 SwingUtilities,它的工作方式与此处的 EventQueue 完全相同。但是,如果我这样做,我的按钮会在其操作期间卡住。

为什么?

最佳答案

通过使用 SwingUtilities.invokeLater,您可以在 Swing 事件线程上调用包含的代码,包括 Thread.sleep(...) 调用,它是你应该永远不要做的事情,因为它会使整个事件线程(负责绘制 GUI 和响应用户输入的线程)进入 hibernate 状态——也就是说,它会卡住你的应用程序。解决方案:使用 Swing Timer相反,或者在后台线程中 sleep 。如果您正在调用长时间运行的代码并使用 Thread.sleep(...) 来模拟它,那么请使用 SwingWorker 为您完成后台工作。请阅读Concurrency in Swing有关详细信息。请注意,没有理由将 SwingUtilities.invokeLater 放在哪里,因为无论如何都会在 EDT(Swing 事件线程)上调用 ActionListener 代码。但是,我会在创建 GUI 的地方使用 SwingUtilities.invokeLater

例如,

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();
      }
   }
}

关于java - 为什么 SwingUtilities.invokeLater() 会导致 JButton 卡住?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31571704/

相关文章:

java - 如何在用户键入时调整 JTable 行的高度?

java - @SuppressWarnings ("unchecked") 的位置是否重要?

java - 如果行为空则保存或更新 Hibernate

java - 用于确认电子邮件的 Struts2 库

java - 基于谓词的计划内存缓存失效

python - 使用 Python 的多线程 MySql 插入

java - JLabel 仅在调整窗口大小后出现在 JFrame 中

java - Swing GUI 布局在 4K 面板中很奇怪

java - 尝试在 servlet 引擎之外使用连接池

java - ConcurrentHashMap 文档说明