11

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

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

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

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

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

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

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

4

5 回答 5

19

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

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

一些常见的方法是使用 Timers 或SwingWorker.

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

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

这是您的情况下 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.
    }
}

To use this code, when the event to modify the list is fired, create a new SwingWorker and tell it to start.

于 2009-06-02T17:49:00.393 回答
7

You are not returning the swing thread properly. I realize you are using callable/runnable but i'm guessing you are not doing it right (although you didn't post enough code to know for sure).

The basic structure would be:

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
    }
}

This is just off the top of my head, but I'm guessing that you either aren't doing the thread.start and are instead calling the run method directly, or you are doing something else in the first method that locks it up (like thread.join). Neither of these would free up the swing thread. The first method MUST return quickly, the run() method can take as long as it wants.

If you are doing a thread.join in the first method, then the thread is NOT being returned to the system!

Edit: (Second edit actually) I think to speak to the problem you are actually feeling--you might want to think more in terms of a model/view/controller system. The code you are writing is the controller (the view is generally considered to be the components on the screen--view/controller are usually very tightly bound).

When your controller gets the event, it should pass the work off to your model. The view is then out of the picture. It does not wait for the model, it's just done.

When your model is finished, it needs to then tell the controller to do something else. It does this through one of the invoke methods. This transfers control back to the controller and you go on your merry way. If you think about it this way, separating control and deliberately passing it back and forth doesn't feel so bulky, and it's actually very common to do it this way.

于 2009-06-02T18:18:09.830 回答
1

It sounds like the problem might be that you are waiting on the threads to finish from inside the GUI thread. Your GUI thread should not wait on these threads, instead you should have the worker threads invoke some method on the GUI thread that sets a flag. When both flags are set then you know both threads finished and you can do the graph.

于 2009-06-02T18:26:14.043 回答
0

I can't really speak to the swing threading model, but:

I have to do the two processing stages in order too, so what is the best way to ensure the second one has waited on the first?

For this kind of functionality, I'd suggest you create two worker threads, and embed a JMS broker. Deliver work to the two threads by passing messages into JMS queues that they read from. Your GUI thread is free to examine the queues to determine when work is happening and represent the state of play in your UI.

于 2009-06-02T18:53:09.033 回答
0

The solution to my problem was a mixture of jjnguy and Bill K's answers, so thanks very much for that guys. I needed to use threads within a SwingWorker like this:

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();  
}

This made sure all the work was being done by worker threads away from the GUI, and also ensuring that the SwingWorker itself wasn't doing all of the work, which might have been a problem.

于 2009-06-03T23:22:45.003 回答