我正在使用线程池大小为1的ThreadPoolExecutor来顺序执行swing worker。我有一个特殊的情况,一个事件到来,创建一个swing工作者进行一些客户端 - 服务器通信,然后更新ui(在done()方法中)。
当用户触发(点击某个项目)某些事件时,这可以正常工作,但如果发生了很多事件则不行。但这发生了,所以我需要取消所有当前正在运行和计划的工作人员。问题是支持ThreadPoolExecutor的队列不知道SwingWorker取消过程(至少看起来像这样)。如此预定的工人被取消,但已经运行的工人得不到。
所以我添加了一个<T extends SwingWorker>
类型的并发队列,只要它们没有被取消就会保存所有worker的引用,当一个新事件到达时,它会在队列中的所有SwingWorkers上调用.cancel(true)。将新的SwingWorker提交给ThreadPoolExecutor。
总结:SwingWorkers是在ThreadPoolExecutor中使用单个Thread创建和执行的。只有最后提交的工作人员才能运行。
有没有其他方法可以解决这个问题,或者这样做是否“好”?
好奇......
答案 0 :(得分:2)
创建仅执行最后一个传入Runnable的单线程ThreadPoolExecutor的一种方法是在添加新的runnable之前,为适当的队列类创建子类并覆盖所有添加方法以清除队列。然后将该队列设置为ThreadPoolExecutor的工作队列。
答案 1 :(得分:0)
为什么你需要一个ThreadPoolExecutor来做这种工作?
你有多少不同的SwingWorkers来源?因为如果源只是一个,你应该使用不同的方法。
例如,您可以定义一个处理一种工作线程的类,并将其链接到一种项目,用户可以在该项目中触发操作并关注该类的单个实例应该运行的事实(例如,使用在完成任务时清除的单例实例)
答案 2 :(得分:0)
您是否可以使用ThreadPoolExecutor来执行客户端 - 服务器通信,然后调用SwingUtilities.invokeLater来更新带有结果的UI,而不是使用SwingWorker?这对我来说似乎更清洁,并确保仍然按顺序处理事件和UI更新。
当您向执行程序提交任务时,您可以保留对其Future实例的引用,以便您可以在需要时取消该任务。
答案 3 :(得分:0)
让我看看我是否正确理解了这个问题。您有一个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
,但这可能不是必需的。