优雅地将队列长度指示符实现到ExecutorServices

时间:2010-02-15 12:29:38

标签: java concurrency queue executorservice executor

为什么,为什么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是通过静态工厂方法获得的,因此无法简单地扩展优秀的单线程执行器并添加队列计数器。那我该怎么做:

  1. 重新发明已在JDK中实现的东西?
  2. 其他聪明的解决方案?

2 个答案:

答案 0 :(得分:54)

有一种更直接的方式:

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newSingleThreadExecutor();
// add jobs
// ...
int size = executor.getQueue().size();

虽然您可能考虑不使用Executor的方便创建方法,而是直接创建执行程序以摆脱强制转换,从而确保执行程序实际上始终是ThreadPoolExecutor,即使Executors.newSingleThreadExecutor的实施有一天会改变。

ThreadPoolExecutor executor = new ThreadPoolExecutor( 1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>() );

这是从JDK 1.6中的Executors.newSingleThreadExecutor直接复制的。传递给构造函数的LinkedBlockingQueue实际上是您将从getQueue返回的对象。

答案 1 :(得分:3)

虽然您可以直接检查队列大小。处理队列过长的另一种方法是使内部队列受限制。

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

参考