Дайте мне посмотреть, правильно ли я понимаю проблему. У вас есть очередь задач FIFO, только самая старая из которых выполняется. Каждое задание должно обновлять интерфейс, когда оно выполнено. Но если наступает определенное пользовательское событие, все задачи должны быть отменены, то есть, должны быть отменены выполняемые задачи, а еще не выполненные задачи должны быть удалены из очереди. Это верно?
Если это так, я бы не стал использовать SwingWorker
, поскольку вам нужен только один рабочий поток, а не один на задачу. FutureTask
должно быть достаточно (при условии, что вы переопределите done()
, чтобы сделать необходимый вызов SwingUtilities.invokeLater()
и выполнить обновление пользовательского интерфейса).
Если вы отмените 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
, но это, вероятно, не обязательно.