ExecutorService,避免任务队列过满的标准方法

时间:2010-02-11 21:12:04

标签: java concurrency

我正在使用ExecutorService来简化并发多线程程序。请参考以下代码:

while(xxx) {
    ExecutorService exService = Executors.newFixedThreadPool(NUMBER_THREADS);
    ...  
    Future<..> ... = exService.submit(..);
    ...
}

在我的情况下,问题是如果所有submit()都被占用,则NUMBER_THREADS不会阻止。结果是任务队列被许多任务淹没。这样做的结果是,使用ExecutorService.shutdown()关闭执行服务需要很长时间(ExecutorService.isTerminated()将长时间为假)。原因是任务队列仍然很满。

现在我的解决方法是使用信号量来禁止在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(..); 
    ...
}

我确信有更好的封装解决方案?

6 个答案:

答案 0 :(得分:27)

诀窍是使用固定的队列大小和:

new ThreadPoolExecutor.CallerRunsPolicy()

我还建议使用番石榴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());
}

更好的一点,您可能需要考虑像RabbitMQ或ActiveMQ这样的MQ,因为它们具有QoS技术。

答案 1 :(得分:5)

你最好自己创建ThreadPoolExecutor(这就是Executors.newXXX()所做的事情。)

在构造函数中,您可以传入BlockingQueue以供Executor用作其任务队列。如果你传入一个大小受限的BlockingQueue(如LinkedBlockingQueue),它应该达到你想要的效果。

ExecutorService exService = new ThreadPoolExecutor(NUMBER_THREADS, NUMBER_THREADS, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(workQueueSize));

答案 2 :(得分:5)

您可以调用ThreadPoolExecutor.getQueue().size()来查找等待队列的大小。如果队列太长,您可以采取措施。如果队列太长而不能减慢生产者的速度(如果合适的话),我建议在当前线程中运行任务。

答案 3 :(得分:4)

一个真正阻塞的ThreadPoolExecutor已经列入许多人的心愿单,甚至还有一个JDC错误。 我遇到了同样的问题,并遇到了这个问题: http://today.java.net/pub/a/today/2008/10/23/creating-a-notifying-blocking-thread-pool-executor.html

它是BlockingThreadPoolExecutor的一个实现,使用RejectionPolicy实现,它使用offer将任务添加到队列,等待队列有空间。看起来不错。

答案 4 :(得分:1)

你可以添加另一个有限大小的bloquing队列来控制executorService中内部队列的大小,有些人认为信号量很容易。 在执行者之前你把()和任务交给()。 take()必须在任务代码

答案 5 :(得分:1)

我知道这太旧了,但可能对其他开发者有用。所以提交solution之一。

正如您要求更好的封装解决方案。它是通过扩展 ThreadPoolExecutor 和覆盖 submit 方法来完成的。

BoundedThreadpoolExecutor 使用信号量实现。当任务队列已满时,Java 执行程序服务会抛出 RejectedExecutionException。使用无界队列可能会导致内存不足错误。这可以通过控制使用执行程序服务提交的任务数量来避免。这可以通过使用信号量或通过实现 RejectedExecutionHandler 来完成。