java 防止 Swing GUI 在后台任务期间锁定
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/940913/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Prevent Swing GUI locking up during a background task
提问by Simonw
I have a swing application which stores a list of objects. When the users clicks a button,
我有一个存储对象列表的 Swing 应用程序。当用户点击按钮时,
I want to perform two operations on each object in the list, and then once that is complete, graph the results in a JPanel. I've been trying SwingWorker, Callable & Runnable to do the processing, but no matter what I do, while processing the list (which can take up to a few minutes, as it is IO bound), the GUI is locked up.
我想对列表中的每个对象执行两个操作,然后一旦完成,将结果绘制在 JPanel 中。我一直在尝试使用 SwingWorker、Callable 和 Runnable 进行处理,但无论我做什么,在处理列表时(可能需要几分钟,因为它受 IO 限制),GUI 被锁定。
I have a feeling it's probably the way I'm calling the threads or something, or could it be to do with the graphing function? That isn't threaded as it is very quick.
我有一种感觉,这可能是我调用线程的方式或其他方式,或者可能与图形功能有关?这不是线程,因为它非常快。
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? I've used join(), and then
我也必须按顺序执行两个处理阶段,那么确保第二个处理阶段等待第一个处理阶段的最佳方法是什么?我用过join(),然后
while(x.isAlive())
{
Thread.sleep(1000);
}
to try and ensure this, but I'm worried this could be the cause of my problem too.
尝试确保这一点,但我担心这也可能是我的问题的原因。
I've been looking everywhere for some pointers, but since I can't find any I'm sure I'm doing something stupid here.
我一直在到处寻找一些指示,但由于我找不到任何指示,我确定我在这里做了一些愚蠢的事情。
回答by jjnguy
The problem is, your long running task is blocking the Thread that keeps the GUI responsive.
问题是,您长时间运行的任务正在阻塞保持 GUI 响应的线程。
What you will need to do is put the long running task on another thread.
您需要做的是将长时间运行的任务放在另一个线程上。
Some common ways of doing this are using Timers or a SwingWorker.
执行此操作的一些常见方法是使用 Timers 或SwingWorker.
The Java tutorialshave lots of information regarding these things in their lesson in concurrency.
在Java教程有很多关于这些东西在他们的并发教训的信息。
To make sure the first task finishes before the second, just put them both on the same thread. That way you won't have to worry about keeping two different threads timed correctly.
为了确保第一个任务在第二个任务之前完成,只需将它们放在同一个线程上。这样你就不必担心保持两个不同的线程正确计时。
Here is a sample implementation of a SwingWorkerFor your case:
这是一个 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 SwingWorkerand tell it to start.
要使用此代码,当修改列表的事件被触发时,创建一个新的SwingWorker并告诉它开始。
回答by Bill K
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).
您没有正确返回摆动线。我意识到你正在使用 callable/runnable 但我猜你做得不对(尽管你没有发布足够的代码来确定)。
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.
这只是我的想法,但我猜你要么没有执行 thread.start 而是直接调用 run 方法,要么在第一个方法中执行其他操作将其锁定(像thread.join)。这些都不会释放摆动线。第一个方法必须快速返回,run() 方法可以根据需要花费多长时间。
If you are doing a thread.join in the first method, then the thread is NOT being returned to the system!
如果您在第一种方法中执行 thread.join,则该线程不会返回到系统!
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.
当您的模型完成后,它需要告诉控制器做其他事情。它通过其中一种调用方法来做到这一点。这会将控制权转移回控制器,然后您就可以继续快乐地工作了。这样想的话,分离控制,故意来回传递也不会觉得那么笨重,而且这样的做法其实很常见。
回答by dotjoe
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.
听起来问题可能在于您正在等待线程从 GUI 线程内部完成。您的 GUI 线程不应等待这些线程,而应让工作线程调用 GUI 线程上设置标志的某些方法。当两个标志都设置时,您就知道两个线程都已完成,您可以绘制图形。
回答by Jherico
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.
对于这种功能,我建议您创建两个工作线程,并嵌入一个 JMS 代理。通过将消息传递到两个线程从中读取的 JMS 队列,将工作交付给这两个线程。您的 GUI 线程可以自由地检查队列以确定何时发生工作并在您的 UI 中表示播放状态。
回答by Simonw
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:
我的问题的解决方案是 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();
}
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.
这确保所有工作都由远离 GUI 的工作线程完成,并确保 SwingWorker 本身没有完成所有工作,这可能是一个问题。

