待機中にThreadPoolExecutorに新しいタスクが追加される可能性があるため、shutdown()
およびawaitTermination()
を使用できません。
そのため、ThreadPoolExecutorがキューを空にし、その前に新しいタスクの追加を停止せずにすべてのタスクを完了するまで待機する方法を探しています。
違いがある場合、これはAndroid向けです。
ありがとう
更新:これを何度か見てから数週間後、この場合、修正されたCountDownLatchの方がうまく機能していることがわかりました。答えはマークしたままにします。これは、私が尋ねたものにさらに当てはまるためです。
特定のタスクが完了したとき、またはタスクの特定のバッチをいつ知りたいかを知りたい場合は、ExecutorService.submit(Runnable)
を使用できます。このメソッドを呼び出すと、Future
オブジェクトが返されます。このオブジェクトは、Collection
に配置でき、メインスレッドは、それぞれのFuture.get()
の呼び出しを繰り返します。これにより、メインスレッドは、ExecutorService
がRunnable
タスクをすべて処理するまで実行を停止します。
Collection<Future<?>> futures = new LinkedList<Future<?>>();
futures.add(executorService.submit(myRunnable));
for (Future<?> future:futures) {
future.get();
}
私のシナリオは、Webサイトから情報を取得して処理するWebクローラーです。多くのページを一度にロードできるため、ThreadPoolExecutorを使用してプロセスを高速化します。クローラーは各ページのハイパーリンクをたどるため、既存のタスクに新しいタスクが作成されます。問題は同じです。メインスレッドは、すべてのタスクがいつ完了するかわからず、結果の処理を開始できます。簡単な方法でこれを判断します。それは非常にエレガントではありませんが、私の場合は動作します:
while (executor.getTaskCount()!=executor.getCompletedTaskCount()){
System.err.println("count="+executor.getTaskCount()+","+executor.getCompletedTaskCount());
Thread.sleep(5000);
}
executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);
タスクのバッチを管理するために CompletionService を探しているのかもしれません。 this answer も参照してください。
(これは、私自身の調整で、Thiloの以前の削除された回答を再現する試みです。)
暗黙の無限条件があるため、質問を明確にする必要があるかもしれません...ある時点でエグゼキューターをシャットダウンすることを決めなければならず、その時点でそれ以上のタスクを受け入れません。あなたの質問は、あなたがknowそれ以上のタスクが送信されないことを待つことを暗示しているようです。これはあなた自身のアプリケーションコードでのみ知ることができます。
次の回答により、(何らかの理由で)新しいTPEにスムーズに移行し、現在送信されているすべてのタスクを完了し、新しいTPEへの新しいタスクを拒否することはできません。あなたの質問に答えるかもしれません。 @Thiloの可能性もあります。
使用中の目に見えるTPEをどこかで定義したと仮定します:
_AtomicReference<ThreadPoolExecutor> publiclyAvailableTPE = ...;
_
その後、TPEスワップルーチンをそのまま記述できます。同期メソッドを使用して作成することもできますが、これは簡単だと思います。
_void rotateTPE()
{
ThreadPoolExecutor newTPE = createNewTPE();
// atomic swap with publicly-visible TPE
ThreadPoolExecutor oldTPE = publiclyAvailableTPE.getAndSet(newTPE);
oldTPE.shutdown();
// and if you want this method to block awaiting completion of old tasks in
// the previously visible TPE
oldTPE.awaitTermination();
}
_
また、スレッドプールを強制的に削除したくない場合は、送信者側が拒否されたタスクにいつか対処する必要があり、新しいTPEにnull
を使用できます。
_void killTPE()
{
ThreadPoolExecutor oldTPE = publiclyAvailableTPE.getAndSet(null);
oldTPE.shutdown();
// and if you want this method to block awaiting completion of old tasks in
// the previously visible TPE
oldTPE.awaitTermination();
}
_
アップストリームの問題を引き起こす可能性があるため、呼び出し元はnull
をどうするかを知る必要があります。
また、単にすべての新しい実行を拒否するダミーのTPEと交換することもできますが、これはTPEでshutdown()
を呼び出した場合と同じです。
shutdown
を使用したくない場合は、以下のアプローチに従ってください。
Future
でのサブミットからすべてのExecutorService
タスクを反復処理し、_Tim Bender
_が示唆するFuture
オブジェクトのget()
オブジェクトのブロック呼び出しでステータスを確認します。
のいずれかを使用
ExecutorService
で invokeAll を使用するExecutors
(since Java 8)の使用executorサービスのinvokeAll()
もCountDownLatch
と同じ目的を達成します
関連するSEの質問:
waitTillDone()をRunnerクラスで呼び出すことができます:
Runner runner = Runner.runner(10);
runner.runIn(2, SECONDS, runnable);
runner.run(runnable); // each of this runnables could submit more tasks
runner.waitTillDone(); // blocks until all tasks are finished (or failed)
// and now reuse it
runner.runIn(500, MILLISECONDS, callable);
runner.waitTillDone();
runner.shutdown();
使用するには、このgradle/maven依存関係をプロジェクトに追加します:'com.github.matejtymes:javafixes:1.0'
詳細については、こちらをご覧ください: https://github.com/MatejTymes/JavaFixes またはこちら: http://matejtymes.blogspot.com/2016/04/executor-that-notifies -you-when-task.html
以下に示すように、キューサイズとアクティブなタスクカウントを使用してみてください。
while (executor.getThreadPoolExecutor().getActiveCount() != 0 || !executor.getThreadPoolExecutor().getQueue().isEmpty()){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}