Java Executors:送信されたタスクを停止するにはどうすればよいですか?
-
07-07-2019 - |
質問
エグゼキュータを使用してタスクを送信しましたが、しばらくしてから(5分など)停止する必要があります。私はこのようにしてみました:
for (Future<?> fut : e.invokeAll(tasks, 300, TimeUnit.SECONDS)) {
try {
fut.get();
} catch (CancellationException ex) {
fut.cancel(true);
tasks.clear();
} catch(ExecutionException ex){
ex.printStackTrace(); //FIXME: gestita con printstack
}
}
しかし、常にエラーが発生します:タスクによって変更され、スレッドによって読み取られる必要がある共有ベクターがあり、すべてのタスクを停止してもタイムアウトが発生した場合:
Exception in thread "Thread-1" java.util.ConcurrentModificationException
何か問題がありますか? 5分後にまだ動作している送信されたタスクを停止するにはどうすればよいですか?
解決
Future
で cancel()
を呼び出したからといって、タスクが自動的に停止するわけではありません。タスクが停止することを確認するには、タスク内でいくつかの作業を行う必要があります。
- タスクに割り込みが送信されるように、
cancel(true)
を使用します。 -
InterruptedException
を処理します。タスク内の関数がInterruptedException
をスローする場合、例外をキャッチしてできるだけ早く正常に終了するようにしてください。 - タスクが連続計算を行う場合、定期的に
Thread.currentThread()。isInterrupted()
をチェックします。
例:
class LongTask implements Callable<Double> {
public Double call() {
// Sleep for a while; handle InterruptedException appropriately
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
System.out.println("Exiting gracefully!");
return null;
}
// Compute for a while; check Thread.isInterrupted() periodically
double sum = 0.0;
for (long i = 0; i < 10000000; i++) {
sum += 10.0
if (Thread.currentThread().isInterrupted()) {
System.out.println("Exiting gracefully");
return null;
}
}
return sum;
}
}
また、他の投稿で言及されているように、スレッドセーフな Vector
クラスを使用している場合でも、 Vector から取得したイテレータにより、
ConcurrentModificationException
code>はスレッドセーフではないため、同期する必要があります。高度なforループはイテレーターを使用するため、注意してください:
final Vector<Double> vector = new Vector<Double>();
vector.add(1.0);
vector.add(2.0);
// Not thread safe! If another thread modifies "vector" during the loop, then
// a ConcurrentModificationException will be thrown.
for (Double num : vector) {
System.out.println(num);
}
// You can try this as a quick fix, but it might not be what you want:
synchronized (vector) { // "vector" must be final
for (Double num : vector) {
System.out.println(num);
}
}
他のヒント
ConcurrentModificationException
は、Exeutorが tasks
Vector <を繰り返し処理している間に
tasks.clear()
を呼び出したときに発生します/ code>。できることは、 shutdownNow()
をExecutorServiceで実行
ConcurrentModificationException
の最も一般的なケースは、 vector
が反復されると同時に修正される場合です。多くの場合、これは単一のスレッドで行われます。反復全体を通じて Vector
をロックする必要があります(デッドロックしないように注意してください)。
fut.get()はブロッキング呼び出しです。タイムアウトした後でも、タスクが完了するまでブロックします。可能な限り5分マークの近くで停止する場合は、割り込みフラグを確認する必要があります。割り込み状態を保持するThread.isInterrupted()メソッドを使用することをお勧めします。すぐに停止するだけで、状態をクリーンアップする必要がない場合は、Futureによってキャッチされ、ExecutionExceptionとして示される例外をスローします。
fut.cancel(true)は、invokeAll()メソッドがすでにこれを行っているため、何もしません。
「タスク」を使用しない限り、他の場所のコレクションでは、おそらくclear()を呼び出す必要はありません。これは、clear()を呼び出すまでにinvokeAll()メソッドがListで実行されるため、問題の原因にはなりません。ただし、実行する新しいタスクのリストの作成を開始する必要がある場合は、古いタスクの新しいリストを使用せずに、新しいタスクのリストを作成することをお勧めします。
残念ながら、私はあなたの問題に対する答えがありません。ここに診断するのに十分な情報がありません。指定したコードスニペットには、ライブラリクラス/メソッドの不適切な(不要なのみの)使用を示すものはありません。おそらく、1行のエラーではなく、完全なスタックトレースを含めた場合。
finallyブロックに fut.cancel(true);
を挿入します