web-dev-qa-db-ja.com

すべてのExecutorServiceタスクが完了してもプログラムがすぐに終了しない

一連の実行可能なオブジェクトをExecutorServiceに入れました。

// simplified content of main method
ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
    threadPool.execute(new Worker());
}

すべてのワーカーが終了した後、プログラム/プロセスがすぐに停止することを期待します。しかし、私のログによると、それが発生するまでにさらに20〜30秒かかります。ワーカーはリソースを割り当てません。実際、現時点では何もしません。

誤解しないでください。これは私にとって重大な問題ではありません。何が起こっているのかを理解しようとしているだけで、これが正常な動作なのかどうか疑問に思っています。

25
alapeno

Executors.newCachedThreadPool()は、ThreadFactoryExecutors.defaultThreadFactory() を使用します。 defaultThreadFactoryのjavadocsによると、「各新しいスレッドは非デーモンスレッドとして作成されます」(強調が追加されています)。したがって、newCachedThreadPool用に作成されたスレッドはデーモンではありません。つまり、JVMが自然に終了するのを防ぎます(「自然に」とは、System.exit(1)を呼び出したり、プログラムを強制終了してJVMを停止させたりできることを意味します)。

アプリがまったく終了する理由は、newCachedThreadPool内で作成された各スレッドがタイムアウトし、非アクティブな時間が経過すると自分自身を閉じるためです。それらの最後の1つが閉じるときに、アプリケーションにデーモン以外のスレッドが残っていない場合、アプリケーションは終了します。

ExecutorService またはshutdownを使用して、shutdownNowを手動で閉じることができます(また、閉じる必要があります)。

デーモン性について説明している Thread のJavaDocも参照してください。

29
yshavit

すべてのワーカーが終了した後、プログラム/プロセスがすぐに停止することを期待します。しかし、私のログによると、それが発生するまでにさらに20〜30秒かかります。ワーカーはリソースを割り当てません。実際、現時点では何もしません。

問題は、ExecutorServiceをシャットダウンしていないことです。すべてのジョブをサービスに送信した後、サービスをシャットダウンする必要があります。そうしないと、サービス内のすべてのスレッドがデーモンスレッドでない限り、JVMは終了しません。スレッドプールをシャットダウンしないと、ExecutorServiceに関連付けられているスレッドがデーモンでない場合は、JVMの終了を停止します。キャッシュされたスレッドプールにタスクを送信した場合、JVMが終了する前に、スレッドがタイムアウトして再度取得されるまで待機する必要があります。

_ExecutorService threadPool = Executors.newCachedThreadPool();
for(int i = 0; i < workerCount; i++) {
    threadPool.execute(new Worker());
}
// you _must_ do this after submitting all of your workers
threadPool.shutdown();
_

デーモンとしてスレッドを開始する可能性が最も高いnotアプリケーションが停止する可能性があるため、実行したいbeforeタスクが完了し、その時点ですべてのタスクが即座に終了する。私は簡単な監査を行っただけで、実稼働コードでExecutorServiceクラスを178回使用しましたが、そのうち2つだけがデーモンスレッドとして開始されました。残りは適切にシャットダウンされます。

アプリケーションの終了時にExecutorServiceを強制的に停止する必要がある場合は、スレッド割り込みフラグを適切に処理してshutdownNow()を使用することが適切です。

javadoc for Executors.newCachedThreadPool()から:

60秒間使用されなかったスレッドは終了し、キャッシュから削除されます。

新しいタスクが送信されないことがわかっている場合は、通常、ExecutorServiceshutdown()を呼び出すことをお勧めします。その後、キュー内のすべてのタスクが完了しますが、サービスはすぐにシャットダウンします。

(または、すべてのタスクが完了したかどうかを気にしない場合-たとえば、メインのUIがなくなった後に無関係なバックグラウンド計算を処理している場合-すべてのスレッドを設定するThreadFactoryを作成できますそのプールでデーモンになります。)

3

基本的に、ExecutorServiceでshutdown()を呼び出してからawaitTermination()を呼び出します。

ExecutorService taskExecutor = Executors.newFixedThreadPool(4);
while(...) {
   taskExecutor.execute(new MyTask());
}
taskExecutor.shutdown();
try {
  taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
  ...
}
3
Danke Xie

ExecutorServiceソリューションのマルチスレッドの場合は、threadPool.shutdown();です。

0
Venkat Alugolu