【问题标题】:My GUI is frozen我的 GUI 被冻结了
【发布时间】:2009-08-07 08:30:16
【问题描述】:

我有些不明白:我的 Swing GUI 包含一个“播放”和“暂停”按钮。我还有一个定义“开”和“关”状态的静态变量。 (主程序生成 GUI)。 通过单击“播放”,我将静态变量的状态更改为“打开”,并在一个同时修改 GUI 的线程中启动了一个耗时的过程。只要静态变量为“ON”,就在同一进程中循环。单击“暂停”会将静态变量更改为关闭。 但是通过点击“播放”,GUI 会冻结,因此:

  1. GUI 不更新
  2. 无法使用我的“暂停”按钮“暂停”进程。

我听说过 EDT 和 SwingWorker,但你有一个简单的方法可以做到。

感谢您的帮助并原谅我的英语不好...

【问题讨论】:

    标签: java multithreading user-interface swing freeze


    【解决方案1】:

    问题是您在负责更新 GUI 的同一线程上执行密集且耗时的工作。 SwingWorker 允许您将耗时的任务移至单独的执行线程,从而使 UI 线程不受约束地执行其任务。

    但是,它确实增加了另一个复杂性:亲和力。调用 UI 组件上的方法通常需要您从 UI 线程执行此操作。因此,您需要使用特殊功能从工作线程返回到 UI 线程。 SwingWorker 也为您提供了这种能力。

    我建议你通读this documentation。

    【讨论】:

      【解决方案2】:

      您需要阅读Concurrency in Swing 以了解 EDT 和 SwingWorkers 的运作方式。

      所有 GUI 更新都在 EDT 上执行,因此当您单击 GUI 组件时,此调用的任何方法都将在 EDT 上执行。如果这是一个耗时的过程,那么这将阻止 EDT 执行任何进一步的 GUI 更新。因此,您的 GUI 冻结,您无法单击暂停按钮。

      您需要使用 SwingWorker 在另一个线程上执行耗时的过程。我在上面提供的链接详细说明了如何执行此操作。

      【讨论】:

        【解决方案3】:

        你不应该在 Swing 的事件处理程序中启动长时间运行的进程,因为它会冻结你的 GUI,你现在知道了。 :) 在新线程中启动它。如果您打算从工作线程操作 GUI(因为 Swing 不是线程安全的),您只需要使用 SwingWorker。

        【讨论】:

          【解决方案4】:

          这是一个非常简单的原因:虽然 Java 正在处理您的耗时过程,但它无法更新 GUI。解决方案:在单独的线程中运行耗时的进程。有很多方法可以对其进行编程,这可能在某种程度上取决于您的程序是如何编写的。

          【讨论】:

            【解决方案5】:

            事件调度线程 (EDT) 是唯一可以安全读取或更新 GUI 的线程。

            暂停按钮应该在事件调度线程中设置开/关变量。

            耗时的操作和循环不应该在 EDT 中。 (循环也不应该连续运行,除了检查变量之外什么都不做,否则它很容易吃掉你所有的 CPU。如果它没有别的事情要做,它应该检查,然后调用 Thread.sleep() 一段时间(比如 100 毫秒).)

            如果您可以证明 on/off 变量被设置为 OFF,但它始终被读取为 ON,则可能是该变量的值没有从 EDT 复制到工作线程。使其volatile 或synchronize 访问它,或使用AtomicReference,或使用SwingUtilities.invokeAndWait() 在EDT 中读取它。

            SwingWorker 可能是最简单的方法,在这里。在doInBackground() 方法中实现您的耗时操作和开/关检查,并在done() 方法中实现您的GUI 更新。

            public enum State {
                RUNNING, STOPPED
            }
            
            public class ThreadSafeStateModel {
                private State state = State.STOPPED;
            
                public synchronized void stop() {
                    state = State.STOPPED;
                }
            
                public synchronized void start() {
                    state = State.RUNNING;
                }
            
                public boolean isRunning() {
                    return state == State.RUNNING;
                }
            }
            
            public class ExpensiveProcessWorker extends SwingWorker<Void, Void> {
            
                private final ThreadSafeStateModel model;
            
                public ExpensiveProcessWorker(ThreadSafeStateModel model) {
                    this.model = model;
                }
            
                @Override // Runs in background
                protected Void doInBackground() throws Exception { 
                    while (model.isRunning()) {
                        // do one iteration of something expensive
                    }
                    return null;
                }
            
                @Override // Runs in event dispatch thread
                protected void done() { 
                    // Update the GUI
                }
            }
            
            public class StopButton extends JButton {
                public StopButton(final ThreadSafeStateModel model) {
                    super(new AbstractAction("Stop") {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            model.stop();
                        }
                    });
                }
            }
            
            public class StartButton extends JButton {
                public StartButton(final ThreadSafeStateModel model) {
                    super(new AbstractAction("Start") {
                        @Override
                        public void actionPerformed(ActionEvent e) {
                            model.start();
                            new ExpensiveProcessWorker(model).execute();
                        }
                    });
                }
            }
            

            (根据实际应用程序可以做很多事情来清理它,但你明白了。)

            【讨论】:

            • 谢谢大卫,一切正常,运行完美。但只是一件事:我不需要使用 done() 方法来刷新 GUI。我成功地在 doInBackground() 方法中做到了。你对此有什么想法吗?
            • 嘿,Jeremy -- 通常更新doInBackground() 中的用户界面通常会起作用,但您不能依赖它。 UI 总是在事件派发线程中绘制,而 UI 模型总是在事件派发线程中读取,因此只在事件派发线程中写入 UI 模型更安全。例如,如果TableModel 中的行数在事件调度线程之外发生了变化,则很有可能在绘制表格时会出现IndexOutOfBoundsExceptions,因为表格和表格模型在行数上存在分歧。
            • 我明白了......现在我尝试添加另一种 GUI 更新,但它冻结了。为了解决这个问题,我使用了 done() 方法来更新 GUI 并使用相同的初始数据创建另一个 swingWorker。现在好了!非常感谢!
            【解决方案6】:
            @Override
            public void actionPerformed(ActionEvent e) {
                 new Thread() {
                    public void run() {
                        //your code which runs on click event
                    }
                }.start();
            
            }
            

            【讨论】:

              猜你喜欢
              • 2012-05-12
              • 2017-01-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2013-01-16
              • 2020-08-06
              • 2015-04-08
              相关资源
              最近更新 更多