防止在后台任务期间锁定Swing GUI

时间:2009-06-02 17:43:45

标签: java multithreading swing swingworker event-dispatch-thread

我有一个存储对象列表的swing应用程序。当用户单击按钮时,

我想对列表中的每个对象执行两个操作,然后在完成后,将结果绘制在JPanel中。我一直在尝试SwingWorker,Callable& Runnable可以进行处理,但无论我做什么,在处理列表时(可能需要几分钟,因为它是IO绑定的),GUI被锁定。

我有一种感觉,这可能是我调用线程或其他东西的方式,还是可能与图形函数有关?这不是线程,因为它非常快。

我必须按顺序完成两个处理阶段,那么确保第二个阶段在第一阶段等待的最佳方法是什么?我使用了join(),然后是

while(x.isAlive())  
{  
        Thread.sleep(1000);  
}

试图确保这一点,但我担心这也可能是我的问题的原因。

我一直在寻找各种指针,但由于我找不到任何东西,我确定我在这里做了些蠢事。

5 个答案:

答案 0 :(得分:18)

问题是,您的长时间运行任务阻止了保持GUI响应的线程。

您需要做的是将长时间运行的任务放在另一个线程上。

执行此操作的一些常用方法是使用计时器或SwingWorker

Java tutorials在他们的并发课程中有很多关于这些事情的信息。

要确保第一个任务在第二个任务之前完成,只需将它们放在同一个线程上即可。这样你就不必担心保持两个不同的线程正确计时。

以下是SwingWorker的示例实现:

public class YourTaskSwingWorkerSwingWorker extends SwingWorker<List<Object>, Void> {
    private List<Object> list
    public YourClassSwingWorker(List<Object> theOriginalList){
        list = theOriginalList;
    }

    @Override
    public List<Object> doInBackground() {
        // Do the first opperation on the list
        // Do the second opperation on the list

        return list;
    }

    @Override
    public void done() {
        // Update the GUI with the updated list.
    }
}

要使用此代码,当触发修改列表的事件时,请创建一个新的SwingWorker并告诉它开始。

答案 1 :(得分:6)

您没有正确返回摆动线程。我意识到你正在使用可调用/可运行但我猜你做得不对(虽然你没有发布足够的代码来确定)。

基本结构如下:

swingMethod() { // Okay, this is a button callback, we now own the swing thread
    Thread t=new Thread(new ActuallyDoStuff());
    t.start();
}

public class ActuallyDoStuff() implements Runnable {
    public void run() {
        // this is where you actually do the work
    }
}

这只是我的头脑,但我猜你要么没有做thread.start而是直接调用run方法,要么你在第一个锁定方法中做了其他事情它(像thread.join)。这些都不会释放挥杆线程。第一种方法必须快速返回,run()方法可以随意返回。

如果您在第一个方法中执行thread.join,则该线程不会返回给系统!

编辑:(实际上是第二次编辑) 我想谈谈你实际感受到的问题 - 你可能想要更多地考虑模型/视图/控制器系统。您正在编写的代码是控制器(视图通常被认为是屏幕上的组件 - 视图/控制器通常非常紧密地绑定)。

当你的控制器获得事件时,它应该将工作传递给你的模型。然后视图就不在了。它不会等待模型,它刚刚完成。

当你的模型完成后,它需要告诉控制器做其他事情。它通过一种调用方法完成此操作。这会将控制权转移回控制器,然后继续你的快乐方式。如果以这种方式考虑它,分离控制并故意来回传递它并不会感觉太笨重,而且这样做实际上很常见。

答案 2 :(得分:1)

听起来问题可能是你正在等待线程从GUI线程内部完成。您的GUI线程不应该等待这些线程,而应该让工作线程在GUI线程上调用一些设置标志的方法。设置两个标志后,您就知道两个线程都已完成,您可以执行图形。

答案 3 :(得分:0)

我无法真正对摇摆线程模型说话,但是:

  

我必须按顺序完成两个处理阶段,那么确保第二个阶段在第一阶段等待的最佳方法是什么?

对于这种功能,我建议你创建两个工作线程,并嵌入一个JMS代理。通过将消息传递到它们读取的JMS队列,将工作交付给两个线程。您的GUI线程可以自由检查队列以确定何时发生工作并表示您的UI中的游戏状态。

答案 4 :(得分:0)

我的问题的解决方案是jjnguy和Bill K的答案的混合,所以非常感谢那些家伙。我需要在SwingWorker中使用这样的线程:

public class Worker extends SwingWorker<Void, Void>   
{  
    private List<Object> list;  
    public YourClassSwingWorker(List<Object> theOriginalList){  
        list = theOriginalList;  
    }

    @Override
    public List<Object> doInBackground() {
        Thread t = new Thread(new ProcessorThread(list));  
        t.start();
    }

    @Override
    public void done() {
        // draw graph on GUI
    }
}  
class ProcessorThread implements Runnable {  
    //do lots of IO stuff  
    Thread t2 = new Thread(new SecondProcess());
    t2.start();  
}

这确保了工作线程远离GUI完成所有工作,并确保SwingWorker本身没有完成所有工作,这可能是一个问题。