2013-03-18 153 views
16

我的ThreadPoolExecutor无法创建新线程。事实上,我写了一个有点冒险的LinkedBlockingQueue,它可以接受任何任务(即它是无限的),但是调用一个额外的处理程序 - 在我的应用程序中发出警告追踪,该池处于后面 - 这给我非常明确的信息,即TPE拒绝即使队列中有数千个条目,也可以创建新线程。我的构造函数如下:ThreadPoolExecutor无界队列无法创建新线程

private final ExecutorService s3UploadPool = 
new ThreadPoolExecutor(1, 40, 1, TimeUnit.HOURS, unboundedLoggingQueue); 

为什么没有创造新主题?

+0

相关问题http://stackoverflow.com/questions/19528304/how-to-get-the-threadpoolexecutor-to-increase-threads-to-max-before-queueing/19528305#19528305 – Gray 2013-10-22 21:38:47

回答

16

这种疑难杂症是覆盖在this blog post:如预期

线程池的这种结构根本不起作用。这是由于ThreadPoolExecutor中的逻辑,如果未能向队列提供任务,则会添加新线程。在我们的例子中,我们使用一个无界的LinkedBlockingQueue,我们可以随时提供一个任务给队列。这实际上意味着我们永远不会超过核心池大小并达到最大池大小。

如果您还需要将最小值与最大池大小分离,则必须进行一些扩展编码。我不知道Java库或Apache Commons中存在的解决方案。解决方案是创建一个知道TPE的耦合的BlockingQueue,并且如果它知道TPE没有可用的线程,然后手动重新请求,它将会拒绝任务。它在链接的文章中有更详细的介绍。最终,你的建设将是这样的:

public static ExecutorService newScalingThreadPool(int min, int max, long keepAliveTime) { 
    ScalingQueue queue = new ScalingQueue(); 
    ThreadPoolExecutor executor = 
     new ScalingThreadPoolExecutor(min, max, keepAliveTime, TimeUnit.MILLISECONDS, queue); 
    executor.setRejectedExecutionHandler(new ForceQueuePolicy()); 
    queue.setThreadPoolExecutor(executor); 
    return executor; 
} 

然而,更简单的设置corePoolSizemaxPoolSize,不要担心这个无稽之谈。

+0

注意,你仍然可以通过允许核心线程超时(您可以从0扩展到最大线程)来获得有限的缩放效果。 – jtahlborn 2013-03-18 20:22:18

+0

根据javadocs:通过设置corePoolSize和maximumPoolSize相同,您可以创建一个固定大小的线程池。所以如果你按照你的最后一句话,最好使用一个Executors.newFixedThreadPool(poolSize)。 – darrickc 2013-04-30 18:55:50

+0

@darrickc看起来像您的修改已被拒绝,但将其添加到评论中将是适当的。我不认为这是有效的,因为在我的用例中,我不希望线程超时。 – djechlin 2013-04-30 19:20:25

3

正如@djechlin所提到的,这是ThreadPoolExecutor定义的行为(令人惊讶到很多)的一部分。我相信我已经找到了解决此问题几分优雅的解决方案,我表现出我的答案在这里:

How to get the ThreadPoolExecutor to increase threads to max before queueing?

基本上你延长LinkedBlockingQueue有它总是queue.offer(...)返回false,这将增加一个如有必要,还可以向池中添加其他线程。如果池已经处于最大线程并且它们都很忙,则将调用RejectedExecutionHandler。这是处理程序,然后put(...)进入队列。

在那里看我的代码。

+1

谢谢。固定。 @kevinarpe。 – Gray 2015-12-25 15:38:58

3

有一个解决此问题的方法。请看下面的实现:

int corePoolSize = 40; 
int maximumPoolSize = 40; 
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 
    60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); 
threadPoolExecutor.allowCoreThreadTimeOut(true); 

通过设置allowCoreThreadTimeOut()true,池中的线程被允许在指定的超时(60秒在这个例子中)之后终止。有了这个解决方案,corePoolSize构造函数参数在实践中决定了最大池大小,因为线程池将长到corePoolSize,然后开始向队列添加作业。池可能永远不会变得比这更大,因为该池在队列满时(假设LinkedBlockingQueue容量可能永远不会发生)不会产生新线程。因此,将maximumPoolSize设置为比corePoolSize更大的值没有多大意义。

注意事项:线程池在超时过期后有0个空闲线程,这意味着在线程创建之前会有一些延迟(通常,您总是会有corePoolSize线程可用)。

更多细节可在ThreadPoolExecutor的JavaDoc中找到。

相关问题