Java 我的GUI冻结了
我有点不明白:我的Swing GUI包含一个“播放”和“暂停”按钮。我还有一个静态变量,用于定义“开”和“关”状态。(主程序生成GUI)。 通过点击“play”,我将静态变量的状态更改为“on”,并在一个线程中启动一个耗时的过程,该线程也会修改GUI。只要静态变量在同一进程中是“开”循环。单击“暂停”会将静态变量更改为关闭。 但通过单击“播放”,GUI将冻结,因此:Java 我的GUI冻结了,java,multithreading,user-interface,swing,freeze,Java,Multithreading,User Interface,Swing,Freeze,我有点不明白:我的Swing GUI包含一个“播放”和“暂停”按钮。我还有一个静态变量,用于定义“开”和“关”状态。(主程序生成GUI)。 通过点击“play”,我将静态变量的状态更改为“on”,并在一个线程中启动一个耗时的过程,该线程也会修改GUI。只要静态变量在同一进程中是“开”循环。单击“暂停”会将静态变量更改为关闭。 但通过单击“播放”,GUI将冻结,因此: GUI不会更新 无法使用我的“暂停”按钮“暂停”进程 我听说过EDT和SwingWorker,但我相信你有一个简单的方法 感谢您的
感谢您的帮助,请原谅我的英语不好…问题是,您在负责更新GUI的同一线程上进行了大量耗时的工作。SwingWorker允许您将耗时的任务移动到单独的执行线程,从而使UI线程不受限制地执行任务 然而,它确实增加了另一个复杂性:亲和性。对UI组件调用方法通常需要从UI线程执行。因此,您需要使用特殊功能从工作线程返回UI线程。SwingWorker也给你这种能力
我建议您通读一遍。您不应该在Swing的事件处理程序中启动长时间运行的进程,因为它会冻结您的GUI,您现在知道了。:)从一个新的线程开始。如果您计划从工作线程操作GUI(因为Swing不是线程安全的),则只需使用
SwingWorker
。这是一个非常简单的原因:当Java正在处理耗时的进程时,它无法更新GUI。解决方案:在单独的线程中运行耗时的进程。编程的方法有很多种,这可能在某种程度上取决于您的程序是如何编写的。您需要阅读以了解EDT和Swingworker是如何操作的
所有GUI更新都在EDT上执行,因此当您单击GUI组件时,此调用的任何方法都将在EDT上执行。如果这是一个耗时的过程,那么这将阻止EDT执行任何进一步的GUI更新。因此,您的GUI处于冻结状态,无法单击“暂停”按钮
您需要使用SwingWorker在另一个线程上执行耗时的进程。我上面提供的链接详细介绍了如何执行此操作。事件调度线程(EDT)是唯一可以安全读取或更新GUI的线程
暂停按钮应在事件分派线程中设置开/关变量
耗时的操作和循环不应在EDT中。(循环也不应该连续运行,除了检查变量之外什么都不做,否则它很容易占用所有的CPU。如果它没有其他事情要做,应该检查,然后调用Thread.sleep()
一段时间(比如100ms)。)
如果您可以证明on/off变量被设置为off,但它始终被读取为on,那么可能是变量的值没有从EDT复制到工作线程。使其易失性
,或同步
对其的访问,或使用原子引用
,或使用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();
}
});
}
}
公共枚举状态{
跑,停
}
公共类ThreadSafeStateModel{
私有状态=State.STOPPED;
公共同步无效停止(){
state=state.STOPPED;
}
公共同步的void start(){
state=state.RUNNING;
}
公共布尔值正在运行(){
返回状态==state.RUNNING;
}
}
公共类ExpensiveProcessWorker扩展SwingWorker{
私有最终ThreadSafeStateModel模型;
public ExpensiveProcessWorker(ThreadSafeStateModel模型){
this.model=模型;
}
@覆盖//在后台运行
受保护的Void doInBackground()引发异常{
while(model.isRunning()){
//做一次昂贵的迭代
}
返回null;
}
@重写//在事件分派线程中运行
受保护的void done(){
//更新GUI
}
}
公共类StopButton扩展了JButton{
公共停止按钮(最终ThreadSafeStateModel型号){
超级(新抽象动作(“停止”){
@凌驾
已执行的公共无效操作(操作事件e){
model.stop();
}
});
}
}
公共类StartButton扩展了JButton{
公共启动按钮(最终ThreadSafeStateModel模型){
超级(新抽象动作(“开始”){
@凌驾
已执行的公共无效操作(操作事件e){
model.start();
新ExpensiveProcessWorker(model).execute();
}
});
}
}
(根据实际的应用程序,我们可以做很多工作来清理这个问题,但您已经知道了。)谢谢,David,一切正常,运行良好。但有一件事:我不需要使用done()方法来刷新GUI。我在doInBackground()方法中成功地做到了这一点。你对此有什么想法吗?嘿,杰里米——一般来说,更新
doInBackground()
中的UI通常会起作用,但你不能依赖它。UI总是在事件分派线程中绘制,UI模型总是在事件分派线程中读取,因此只在事件分派线程中写入UI模型更安全。例如,如果TableModel
中的行数在事件分派线程之外发生更改,则很有可能在
@Override
public void actionPerformed(ActionEvent e) {
new Thread() {
public void run() {
//your code which runs on click event
}
}.start();
}