並行マルチスレッドプログラムを簡単にするためにExecutorService
を使用しています。次のコードを取ります:
_while(xxx) {
ExecutorService exService = Executors.newFixedThreadPool(NUMBER_THREADS);
...
Future<..> ... = exService.submit(..);
...
}
_
私の場合、問題は、すべての_NUMBER_THREADS
_が使用されている場合、submit()
がブロックされないことです。その結果、タスクキューは多くのタスクでいっぱいになります。この結果、ExecutorService.shutdown()
を使用して実行サービスをシャットダウンするには時間がかかります(ExecutorService.isTerminated()
は長い間falseになります)。理由は、タスクキューがまだ非常にいっぱいであることです。
当面の回避策は、セマフォを使用して、ExecutorService
のタスクキュー内に多数のエントリが存在することを禁止することです。
_...
Semaphore semaphore=new Semaphore(NUMBER_THREADS);
while(xxx) {
ExecutorService exService = Executors.newFixedThreadPool(NUMBER_THREADS);
...
semaphore.aquire();
// internally the task calls a finish callback, which invokes semaphore.release()
// -> now another task is added to queue
Future<..> ... = exService.submit(..);
...
}
_
より良いカプセル化されたソリューションがあると確信していますか?
トリックは、固定キューサイズを使用することです。
new ThreadPoolExecutor.CallerRunsPolicy()
Guavaの ListeningExecutorService の使用もお勧めします。以下は、コンシューマー/プロデューサーキューの例です。
private ListeningExecutorService producerExecutorService = MoreExecutors.listeningDecorator(newFixedThreadPoolWithQueueSize(5, 20));
private ListeningExecutorService consumerExecutorService = MoreExecutors.listeningDecorator(newFixedThreadPoolWithQueueSize(5, 20));
private static ExecutorService newFixedThreadPoolWithQueueSize(int nThreads, int queueSize) {
return new ThreadPoolExecutor(nThreads, nThreads,
5000L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(queueSize, true), new ThreadPoolExecutor.CallerRunsPolicy());
}
QoSテクノロジを備えているRabbitMQやActiveMQのようなMQを検討することをお勧めします。
ThreadPoolExecutor.getQueue().size()
を呼び出して、待機キューのサイズを確認できます。キューが長すぎる場合は、アクションを実行できます。キューが長すぎてプロデューサーをスローダウンできない場合は、現在のスレッドでタスクを実行することをお勧めします(適切な場合)。
ThreadPoolExecutor を自分で作成する方がよい(とにかく、Executors.newXXX()が行うことです)。
コンストラクターでは、Executorがタスクキューとして使用するBlockingQueueを渡すことができます。サイズ制約のあるBlockingQueue( LinkedBlockingQueue など)を渡すと、必要な効果が得られます。
ExecutorService exService = new ThreadPoolExecutor(NUMBER_THREADS, NUMBER_THREADS, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(workQueueSize));
真のブロッキングThreadPoolExecutorは、多くのウィッシュリストに載っています。JDCバグも開かれています。私は同じ問題に直面していて、これに遭遇しました: http://today.Java.net/pub/a/today/2008/10/23/creating-a-notifying-blocking-thread-pool -executor.html
これはBlockingThreadPoolExecutorの実装であり、RejectionPolicyを使用して実装され、offerを使用してタスクをキューに追加し、キューにスペースができるのを待機します。よさそうです。
サイズが制限されている別のブローキューを追加して、executorServiceの内部キューのサイズを制御することができます。エグゼキュータの前にput()を実行し、タスクを取得してtake()を実行します。 take()はタスクコード内にある必要があります