web-dev-qa-db-ja.com

適切にシャットダウンする方法Java ExecutorService

いくつかのタスクオブジェクトを実行する単純なJava ExecutorServiceがあります(Callableを実装します)。

_ExecutorService exec = Executors.newSingleThreadExecutor();
List<CallableTask> tasks = new ArrayList<>();
// ... create some tasks
for (CallableTask task : tasks) {
 Future future = exec.submit(task);
 result = (String) future.get(timeout, TimeUnit.SECONDS);
 // TASKS load some classes and invoke their methods (they may create additional threads)
 // ... catch interruptions and timeouts
}
exec.shutdownNow();
_

すべてのタスクが終了した後(DONEまたはTIMEOUT-ed)、エグゼキュータをシャットダウンしようとしましたが、停止しません:exec.isTerminated() = FALSE.タイムアウトした一部のタスクが適切に終了していないと思われます。

そして、はい、私はエグゼキュータのシャットダウンが何も保証していないことを知っています:

アクティブに実行されているタスクの処理を停止するための最善の試み以外の保証はありません。たとえば、一般的な実装は{@link Thread#interrupt}を介してキャンセルされるため、割り込みに応答しないタスクは決して終了しない可能性があります。

私の質問は、それらの(タスク)スレッドを確実に終了させる方法はありますか?私が思いついた最善の解決策は、プログラムの最後にSystem.exit()を呼び出すことですが、それはまったくばかげています。

10
N10

ExecutorService のOracleAPIドキュメントページからの推奨方法:

 void shutdownAndAwaitTermination(ExecutorService pool) {
   pool.shutdown(); // Disable new tasks from being submitted
   try {
     // Wait a while for existing tasks to terminate
     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
       pool.shutdownNow(); // Cancel currently executing tasks
       // Wait a while for tasks to respond to being cancelled
       if (!pool.awaitTermination(60, TimeUnit.SECONDS))
           System.err.println("Pool did not terminate");
     }
   } catch (InterruptedException ie) {
     // (Re-)Cancel if current thread also interrupted
     pool.shutdownNow();
     // Preserve interrupt status
     Thread.currentThread().interrupt();
   }

プールのシャットダウンに時間がかかる場合は、変更できます

1f (!pool.awaitTermination(60, TimeUnit.SECONDS))

while (!pool.awaitTermination(60, TimeUnit.SECONDS))

シャットダウン関連の方法の簡単な要約

shutdown()

以前に送信されたタスクが実行される正常なシャットダウンを開始しますが、新しいタスクは受け入れられません。

shutdownNow()

アクティブに実行されているすべてのタスクの停止を試み、待機中のタスクの処理を停止し、実行を待機していたタスクのリストを返します。

awaitTermination(long timeout、TimeUnit unit)throws InterruptedException

シャットダウン要求後にすべてのタスクの実行が完了するか、タイムアウトが発生するか、現在のスレッドが中断されるまで、どちらか早い方でブロックします。

15
Ravindra babu

それらのタスクを制御できますか?つまり、自分で作成していますか?それらのどこかでスレッド割り込みが無視されているのではないかと思います。

try {
  ....
}
catch {InterruptedException e) {
   // do nothing
}

InterruptedExceptionがスローされた場合、スレッドの割り込みフラグをリセットする必要があります。そうしないと、スレッドは終了しません。 詳細はこちら を参照してください。

残念ながら、これに従わないライブラリを使用している可能性があります。その場合、これを簡単に回避することはできません。その場合、重要なオプションの1つは、サブプロセスをフォークしてCallableのジョブを実行することです。これにより、プロセスの終了時にすべてのリソースがクリアされます。重量級で、おそらく重要ですが、信頼性があります。

5
Brian Agnew