web-dev-qa-db-ja.com

ExecutorServicesへのキュー長インジケーターのエレガントな実装

なぜ、ああ、なぜJava.util.concurrentそのExecutorServicesのキュー長インジケーターを提供しますか?最近、私は自分が次のようなことをしていることに気づきました。

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配列を思い出させます)。ただし、ExecutorServicesは静的ファクトリメソッドを介して取得されるため、他の点では優れたシングルスレッドエグゼキュータを単純に拡張してキューカウンタを追加する方法はありません。だから私は何をすべきか:

  1. JDKにすでに実装されているものを再発明しますか?
  2. 他の賢い解決策?
34
Joonas Pulakka

より直接的な方法があります:

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から返されるオブジェクトそのものです。

58
x4u

キューサイズを直接確認できますが。長くなりすぎているキューを処理する別の方法は、内部キューを制限することです。

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 を参照してください。

参考文献

4
Alex