防止 Swing GUI 在后台任务期间锁定
-
06-09-2019 - |
题
我有一个 Swing 应用程序,它存储对象列表。当用户点击按钮时,
我想对列表中的每个对象执行两个操作,然后完成后在 JPanel 中绘制结果图。我一直在尝试使用 SwingWorker、Callable 和 Runnable 来进行处理,但无论我做什么,在处理列表时(可能需要几分钟,因为它是 IO 绑定的),GUI 都会被锁定。
我有一种感觉,这可能是我调用线程或其他东西的方式,或者可能与绘图函数有关?这不是线程,因为它非常快。
我也必须按顺序执行两个处理阶段,那么确保第二个处理阶段等待第一个处理阶段的最佳方法是什么?我用过 join(),然后
while(x.isAlive())
{
Thread.sleep(1000);
}
试图确保这一点,但我担心这也可能是我的问题的原因。
我一直在到处寻找一些指示,但由于我找不到任何指示,所以我确信我在这里做了一些愚蠢的事情。
解决方案
问题是,您的长时间运行的任务正在阻塞保持 GUI 响应的线程。
您需要做的是将长时间运行的任务放在另一个线程上。
执行此操作的一些常见方法是使用计时器或 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.
}
}
要使用此代码,当触发修改列表的事件时,创建一个新的 SwingWorker
并告诉它开始。
其他提示
您没有正确返回Swing线程。我知道你正在使用的可调用/运行的,但我猜你不这样做是正确的(虽然你没有张贴足够的代码肯定知道)。
的基本结构是:
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)。这些都不将腾出的Swing线程。第一种方法必须迅速返回,只要它想要的run()方法可以采用。
如果您是在第一种方法做的Thread.join,那么线程在返回到系统!
编辑:(第二编辑实际上) 我想和你谈谈实际感受的问题 - 你可能需要考虑更多的模型/视图/控制器系统方面。你正在写的代码是控制器(视图一般被认为是在屏幕上的组件 - 视图/控制器通常很紧密结合)
。当您的控制器获取的情况下,它应该通过工作给您的模型。该视图然后出来的图片。它不会等待模式,它只是做了。
当你的模型就完成了,它需要再告诉控制器到别的做一些事情。它通过的调用方法之一。这将控制权返回给控制器,你去你的快乐的方式。如果你仔细想想这样,分开控制,强行通行来回不觉得如此庞大,它实际上是非常普遍的做这种方式。
这听起来像问题可能是你正在等待的线程从GUI线程内完成。您的GUI线程不应该等待这些线程,而不是你应该有工作线程调用GUI线程设置一个标志上的一些方法。当两个标志设置,那么,你知道,这两个线程完成,你可以做图形。
我真的不能说的摆动线程模型,但是:
我必须做的两个处理阶段,以便太多,所以什么是保证第二个已经等待第一个最好的方法是什么?
有关这种功能,我建议你创建两个工作线程,并嵌入一个JMS代理。通过传递消息到他们从读取JMS队列传递工作的两个线程。您的GUI线程是免费检查队列以确定工作时发生的事情,并表示在UI的发挥状态。
我的问题的解决方案是jjnguy和比尔·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类本身没有做完所有的工作,这可能是一个问题。