なぜ、ああ、なぜJava.util.concurrent
そのExecutorService
sのキュー長インジケーターを提供しますか?最近、私は自分が次のようなことをしていることに気づきました。
ExecutorService queue = Executors.newSingleThreadExecutor();
AtomicInteger queueLength = new AtomicInteger();
...
public void addTaskToQueue(Runnable runnable) {
if (queueLength.get() < MAX_QUEUE_LENGTH) {
queueLength.incrementAndGet(); // Increment queue when submitting task.
queue.submit(new Runnable() {
public void run() {
runnable.run();
queueLength.decrementAndGet(); // Decrement queue when task done.
}
});
} else {
// Trigger error: too long queue
}
}
これは問題なく動作しますが...これは実際にはExecutorService
の一部として実装する必要があると思います。カウンターを持ち歩くのはばかげてエラーが発生しがちです分離された実際のキューから、その長さはカウンターが示すことになっています(C配列を思い出させます)。ただし、ExecutorService
sは静的ファクトリメソッドを介して取得されるため、他の点では優れたシングルスレッドエグゼキュータを単純に拡張してキューカウンタを追加する方法はありません。だから私は何をすべきか:
より直接的な方法があります:
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newSingleThreadExecutor();
// add jobs
// ...
int size = executor.getQueue().size();
Executorの便利なcreateメソッドを使用しないことを検討するかもしれませんが、キャストを取り除くためにexecutorを直接作成するため、Executors.newSingleThreadExecutor
の実装であっても、executorが常に実際にはThreadPoolExecutor
になるようにしてください。いつか変わるだろう。
ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>() );
これは、JDK1.6のExecutors.newSingleThreadExecutor
から直接コピーされます。コンストラクターに渡されるLinkedBlockingQueue
は、実際にはgetQueue
から返されるオブジェクトそのものです。
キューサイズを直接確認できますが。長くなりすぎているキューを処理する別の方法は、内部キューを制限することです。
public static
ExecutorService newFixedThreadPoolWithQueueSize(int nThreads, int queueSize) {
return new ThreadPoolExecutor(nThreads, nThreads,
5000L, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(queueSize, true));
}
制限を超えると、RejectedExecutionExceptions( this を参照)が発生します。
例外を回避したい場合は、呼び出し元のスレッドを乗っ取って関数を実行できます。解決策については、 this SO question を参照してください。