使用ThreadPoolExecutor的SwingWorker取消
我正在使用一个线程池大小为1的ThreadPoolExecutor来顺序执行swing工作。我遇到了一个特殊情况,一个事件到达时创建一个执行某些客户端 - 服务器通信的swing工作人员,然后更新ui(在done()方法中)。使用ThreadPoolExecutor的SwingWorker取消
当用户触发(点击某个项目)某些事件时,这种方式可以正常工作,但如果发生很多事件,则不会发生这种情况。但是,这发生,所以我需要取消所有当前运行和计划的工人。问题是支持ThreadPoolExecutor的队列没有意识到SwingWorker的取消过程(至少看起来像这样)。如此计划的工人会被取消,但已经在运行的工人不会。
,所以我说<T extends SwingWorker>
类型的并发队列,只要它们不取消,当一个新的事件到达它调用.cancel(真)在队列中的所有SwingWorkers持有所有工人的参考,并提交新SwingWorker到ThreadPoolExecutor。
摘要:SwingWorkers是使用单个线程在ThreadPoolExecutor中创建和执行的。只有最后提交的工人应该正在运行。
有没有其他解决方案可以解决这个问题,还是可以这样做吗?
只是好奇...
一种创建单线程的方法ThreadPoolExecutor只执行最后一次传入的Runnable,是为了创建一个合适的队列类并覆盖所有添加方法以在添加新的可运行队列之前清除队列。然后将该队列设置为ThreadPoolExecutor的工作队列。
为什么你需要一个的ThreadPoolExecutor做这种工作?
你有多少种不同的SwingWorkers来源?因为如果源只是一个,你应该使用不同的方法。
例如,您可以定义一个类来处理一种工作线程,并将其链接到一种类型的项目上,用户可以在该类项目上触发操作并关注该类内部的事实,即线程的单个实例应该是运行(例如使用完成任务时清除的单例实例)
我需要一个带有单线程的线程池执行程序来确保一次只能运行一个任务。但他们中的很多人都没有,所以我必须排队。 – MrWhite 2009-06-22 10:12:34
而不是使用SwingWorker,您不能使用ThreadPoolExecutor执行客户端 - 服务器通信,然后调用SwingUtilities.invokeLater以更新UI结果?这对我来说似乎有点干净,并且可以确保仍然按顺序处理事件和UI更新。
当您向执行者提交任务时,您可以保留对其Future实例的引用,以便您可以根据需要取消该任务。
问题是,我可以从执行程序获得的未来不会像摆动工作人员那样提供取消可能性,因为它只是包装可运行接口。 – MrWhite 2009-06-22 10:11:48
让我看看我是否正确理解问题。您有一个任务的FIFO队列,只有最早的一个正在运行。每个任务都需要在UI完成时更新。但是,如果某个用户事件进入,则需要取消所有任务 - 即需要取消正在运行的任务,并且尚未运行的任务需要从队列中移除。是对的吗?
假设是这样,我不会使用SwingWorker
,因为您只需要一个工作线程,而不是每个任务一个。 FutureTask
应该足够了(假设您覆盖done()
对SwingUtilities.invokeLater()
进行必要的调用并执行UI更新)。
如果您取消FutureTask
,那么即使其run()
方法被调用,它也不会执行任何操作。因此,您可以将FutureTask
安全地提交给ExecutorService
,因为知道即使执行者尝试运行它们,取消也会起作用。
我怀疑一个足够好的解决办法只是让所有FutureTasks
可能需要取消的名单,并取消所有当用户事件用武之地。ExecutorService
仍然会尝试运行它们,但它基本上是没有任何操作。您需要确保已完成的任务已从列表中删除,并且您需要确保列表已更新并以线程安全的方式使用(可能来自将任务放在ExecutorService
上的同一线程),但这不应该太难了。
我在短短一个小时内就把代码弄糟了,我不打赌它是正确的,但你明白了。 :)
/** Untested code! Use at own risk. */
public class SwingTaskExecutor {
// ////////////////////////////////////////////////////////////
// Fields
private final ExecutorService execSvc = Executors.newFixedThreadPool(1);
private final Lock listLock = new ReentrantLock();
private final List<ManagedSwingTask<?>> activeTasks =
new ArrayList<ManagedSwingTask<?>>();
// ////////////////////////////////////////////////////////////
// Public methods
public <T> Future<T> submit(SwingTask<T> task) {
ManagedSwingTask<T> managedTask = new ManagedSwingTask<T>(task);
addToActiveTasks(managedTask);
execSvc.submit(managedTask);
return managedTask;
}
public void cancelAllTasks() {
listLock.lock();
try {
for (ManagedSwingTask<?> t: activeTasks) {
t.cancel(true);
}
activeTasks.clear();
} finally {
listLock.unlock();
}
}
// ////////////////////////////////////////////////////////////
// Private methods
private <T> void addToActiveTasks(ManagedSwingTask<T> managedTask) {
listLock.lock();
try {
activeTasks.add(managedTask);
} finally {
listLock.unlock();
}
}
// ////////////////////////////////////////////////////////////
// Helper classes
private class ManagedSwingTask<T> extends FutureTask<T> {
private final SwingTask<T> task;
ManagedSwingTask(SwingTask<T> task) {
super(task);
this.task = task;
}
@Override
public void cancel(boolean mayInterruptIfRunning) {
try {
task.cancel();
} finally {
super.cancel(mayInterruptIfRunning);
}
}
@Override
protected void done() {
removeFromActiveTasks();
updateUIIfDone();
}
private void removeFromActiveTasks() {
listLock.lock();
try {
activeTasks.remove(this);
} finally {
listLock.unlock();
}
}
private void updateUIIfDone() {
if (isDone()) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
task.updateUI();
}
});
}
}
}
public static interface SwingTask<T> extends Callable<T> {
/** Called from the EDT if task completes successfully */
void updateUI();
/** Hook in case there's task-specific cancellation to be done*/
void cancel();
}
}
这样的事情,无论如何。
如果您想双倍肯定,可以关闭并更换ExecutorService
,但这可能不是必需的。
它工作吗?如果它没有损坏不修复它(或修复它,直到它,大声笑) – Gandalf 2009-06-17 16:57:12
是的,它的工作原理。但是编写并发代码很困难。我只想知道我的任务是否存在任何概念。 – MrWhite 2009-06-17 20:39:39