为什么SwingUtilities.invokeLater()导致JButton冻结? [英] Why does SwingUtilities.invokeLater() cause JButton to freeze?

查看:68
本文介绍了为什么SwingUtilities.invokeLater()导致JButton冻结?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑这个基本的Swing程序,该程序由两个按钮组成:

Consider this basic Swing program, consisting out of two buttons:

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()放入Thread并调用start()来做到这一点.但是我已经读过我应该改用SwingUtilities,它的工作原理与这里的EventQueue完全一样.但是,如果这样做,我的Button将在其操作期间冻结.

I want my second button testSystemOut to be usable while the first one is working on its long action (here, I put a 3 second sleep in it). I can do that by manually putting doLongAction() in a Thread and call start(). But I've read I should use SwingUtilities instead, which works exactly like EventQueue here. However, if I do so, my Button freezes for the duration of its action.

为什么?

推荐答案

使用SwingUtilities.invokeLater,您将在Swing事件线程上调用包含的代码,包括Thread.sleep(...)调用,这是您应该 >从不执行任何操作,因为它会将整个事件线程(负责绘制GUI和响应用户输入的线程)置于休眠状态,即冻结了您的应用程序.解决方案:改用 Swing Timer 或在后台线程.如果您要调用长时间运行的代码并使用Thread.sleep(...)对其进行仿真,请使用SwingWorker为您完成后台工作.有关详细信息,请阅读 Swing中的并发.请注意,没有理由将SwingUtilities.invokeLater放在其中,因为无论如何,都会在EDT(Swing事件线程)上调用ActionListener代码.但是,我会在创建GUI的地方使用SwingUtilities.invokeLater.

By using SwingUtilities.invokeLater, you are calling the enclosed code, including the Thread.sleep(...) call, on the Swing event thread, which is something you should never do since it puts the entire event thread, the thread responsible for drawing your GUI's and responding to user input, to sleep -- i.e., it freezes your application. Solution: use a Swing Timer instead or do your sleeping in a background thread. If you are calling long-running code and using a Thread.sleep(...) to simulate it, then use a SwingWorker to do your background work for you. Please read Concurrency in Swing for the details on this. Note that there is no reason for the SwingUtilities.invokeLater where you have it since the ActionListener code will be called on the EDT (the Swing event thread) regardless. I would however use SwingUtilities.invokeLater where you create your GUI.

例如

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

这篇关于为什么SwingUtilities.invokeLater()导致JButton冻结?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆