ThreadPoolExecutorを使用したSwingWorkerのキャンセル
-
06-07-2019 - |
質問
iスレッドプールサイズが1のThreadPoolExecutorを使用して、スイングワーカーを順次実行しています。クライアントサーバー間通信を行うSwingワーカーを作成し、その後(done()メソッドで)uiを更新するイベントが到着する特別なケースがありました。
これは、ユーザーがいくつかのイベントを発生(アイテムをクリック)したときに正常に機能しますが、多くのイベントが発生した場合は機能しません。しかし、これは発生するため、現在実行中およびスケジュールされているすべてのワーカーをキャンセルする必要があります。問題は、ThreadPoolExecutorをバッキングしているキューがSwingWorkerキャンセルプロセスを認識していないことです(少なくともそのように思われます)。そのため、スケジュールされたワーカーはキャンセルされますが、既に実行中のワーカーはキャンセルされません。
したがって、キャンセルされない限りすべてのワーカーの参照を保持するタイプ<T extends SwingWorker>
の並行キューを追加し、新しいイベントが到着すると、キュー内のすべてのSwingWorkerで.cancel(true)を呼び出して、 ThreadPoolExecutorへの新しいSwingWorker。
概要:SwingWorkersは、単一のスレッドを持つThreadPoolExecutorで作成および実行されます。最後に送信されたワーカーのみが実行されている必要があります。
この問題を解決する代替手段はありますか?<!> quot; ok <!> quot;このようにするには?
好奇心が強い...
解決
最後の着信Runnableのみを実行するシングルスレッドThreadPoolExecutorを作成する1つの方法は、適切なキュークラスをサブクラス化し、すべての追加メソッドをオーバーライドして、新しいrunnableを追加する前にキューをクリアすることです。次に、そのキューをThreadPoolExecutorの作業キューとして設定します。
他のヒント
なぜこのような仕事をするのにThreadPoolExecutorが必要なのですか?
異なるSwingWorkerのソースはいくつありますか?ソースが1つだけの場合は、別のアプローチを使用する必要があるためです。
たとえば、1種類の作業スレッドを処理するクラスを定義できます。このクラスは、ユーザーがアクションを起動し、そのクラス内でスレッドの1つのインスタンスが実行されることを確認できる1種類のアイテムにリンクされます(たとえば、タスクの終了時にクリアされるシングルトンインスタンスを使用)
SwingWorkerを使用する代わりに、ThreadPoolExecutorを使用してクライアント/サーバー通信を実行し、SwingUtilities.invokeLaterを呼び出して結果でUIを更新することはできませんか?私にはこれは少しきれいに見え、イベントとUIの更新が順番どおりに処理されるようにします。
タスクをエグゼキューターに送信するとき、必要に応じてタスクをキャンセルできるように、Futureインスタンスへの参照を保持できます。
問題を正しく理解しているかどうかを確認します。タスクのFIFOキューがあり、最も古いタスクのみが実行されています。各タスクは、完了したらUIを更新する必要があります。ただし、特定のユーザーイベントが発生した場合、すべてのタスクをキャンセルする必要があります。つまり、実行中のタスクをキャンセルし、まだ実行されていないタスクをキューから削除する必要があります。そうですか?
仮に、タスクごとにではなく1つのワーカースレッドしか必要ないので、SwingWorker
は使用しません。 FutureTask
で十分です(done()
をオーバーライドしてSwingUtilities.invokeLater()
に必要な呼び出しを行い、UIの更新を行うと仮定します)。
run()
をキャンセルすると、そのExecutorService
メソッドが呼び出されても、何も実行されません。したがって、エグゼキュータがキャンセルを実行しようとしてもキャンセルが機能することを認識して、FutureTasks
に安全に<=>を送信できます。
十分な解決策は、キャンセルする必要がある可能性があるすべての<=>のリストを保持し、ユーザーイベントが発生したときにそれらをすべてキャンセルすることだと思います。<=>は引き続き実行しようとしますそれらは基本的に何もしません。完了したタスクがリストから削除されていることを確認する必要があり、リストがスレッドセーフな方法で更新され、使用されていることを確認する必要があります(おそらく<=>にタスクを配置する同じスレッドから)難しくありません。
以下のコードをたった1時間で打ち消しましたが、それが正しいとは思わないでしょうが、あなたはその考えを知っています。 :)
/** 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();
}
}
とにかくそのようなもの。
もう一度確認したい場合は、シャットダウンして<=>を置き換えることができますが、おそらく必要ではありません。