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

Java 防止后台任务期间Swing GUI锁定,java,multithreading,swing,swingworker,event-dispatch-thread,Java,Multithreading,Swing,Swingworker,Event Dispatch Thread,我有一个swing应用程序,它存储对象列表。当用户点击一个按钮时 我想对列表中的每个对象执行两个操作,完成后,在JPanel中绘制结果。我一直在尝试SwingWorker、Callable&Runnable进行处理,但无论我做什么,在处理列表时(可能需要几分钟,因为它是IO绑定的),GUI都被锁定 我有一种感觉,这可能是我调用线程或其他东西的方式,或者可能与图形功能有关?这不是线程,因为它非常快 我也必须按顺序完成这两个处理阶段,那么确保第二个处理阶段等待第一个处理阶段的最佳方法是什么?我使用了

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

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

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

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

while(x.isAlive())  
{  
        Thread.sleep(1000);  
}
试图确保这一点,但我担心这也可能是我问题的原因


我一直在到处寻找一些指针,但由于找不到任何指针,我确信我在这里做了一些愚蠢的事情。

问题是,您长期运行的任务正在阻塞线程,从而使GUI保持响应

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

一些常用的方法是使用定时器或定时器

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

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

以下是您案例中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.
    }
}
public类YourTaskSwingWorker SwingWorker扩展SwingWorker{
私人名单
public YourClassSwingWorker(列出原始列表){
列表=原始列表;
}
@凌驾
公共列表doInBackground(){
//做列表上的第一个操作
//做清单上的第二项操作
退货清单;
}
@凌驾
公众假期结束(){
//使用更新的列表更新GUI。
}
}

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

您没有正确返回swing线程。我知道您正在使用callable/runnable,但我猜您做得不对(尽管您没有发布足够的代码来确定)

基本结构将是:

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,则该线程不会返回到系统

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

当您的控制器获得事件时,它应该将工作传递给您的模型。然后视图就消失了。它不等待模型,它只是完成


当您的模型完成时,它需要告诉控制器执行其他操作。它通过一个调用方法来实现这一点。这将把控制权转移回控制器,你就可以继续快乐的生活了。如果你这样想的话,分离控件并有意地来回传递它并不会让你觉得太笨重,事实上这样做很常见。

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

我不能真正谈论swing线程模型,但是:

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


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

我的问题的解决方案是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();  
}
公共类Worker扩展SwingWorker
{  
私人名单;
public YourClassSwingWorker(列表理论原始列表){
列表=原始列表;
}
@凌驾
公共列表doInBackground(){
线程t=新线程(新处理器读取(列表));
t、 start();
}
@凌驾
公众假期结束(){
//在GUI上绘制图形
}
}  
类ProcessorThread实现可运行的{
//做很多事情
线程t2=新线程(新的SecondProcess());
t2.start();
}

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

我很确定这个问题已经提到了线程和SwingWorker。另外,SwingUtilities.invokeLater()在GUI线程上运行。是的,我给出了一个类似于“通用swing线程”的答案……有点糟糕,我昨天懒得查找Javadoc,但我现在看到第二个类型参数仅用作publish()和process()的参数。我收回我的第一条评论