我的GUI被冻结了

时间:2009-08-07 08:30:16

标签: java multithreading user-interface swing freeze

我有一些我无法理解的东西:我的Swing GUI包含一个'播放'和'暂停'按钮。我还有一个定义'ON'和'OFF'状态的静态变量。 (主程序生成GUI)。 通过克服'play',我将静态变量的状态更改为'ON',并在一个也修改GUI的线程中启动一个耗时的过程。只要静态变量在同一进程中为'ON'循环。单击“暂停”会将静态变量更改为OFF。 但是通过点击“播放”,GUI就会冻结,因此:

  1. GUI不会更新
  2. 使用“暂停”按钮无法“暂停”此过程。
  3. 我听说过EDT和SwingWorker但是我有一个简单的方法可以做到这一点。

    感谢您的帮助并原谅我糟糕的英语......

6 个答案:

答案 0 :(得分:6)

问题是你正在负责更新GUI的同一个线程上进行密集,耗时的工作。 SwingWorker允许您将耗时的任务移动到单独的执行线程中,从而使UI线程不受限制地执行其操作。

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

我建议您通读this documentation

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

耗时的操作和循环应该在EDT中。 (循环也不应该一直运行,除了检查变量之外什么也不做,或者它可以很容易地占用你的所有CPU。如果没有别的办法,它应该检查,然后调用Thread.sleep()一段时间(说100毫秒)。)

如果你可以证明开/关变量被设置为OFF,但是它总是被读为ON,那么可能是变量的值没有从EDT复制到工作线程。对其进行volatilesynchronize访问,或使用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();
            }
        });
    }
}

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

答案 5 :(得分:0)

@Override
public void actionPerformed(ActionEvent e) {
     new Thread() {
        public void run() {
            //your code which runs on click event
        }
    }.start();

}