我的ThreadPoolExecutor
无法创建新主题。事实上,我写了一个有点hacky LinkedBlockingQueue
,它将接受任何任务(即它是无限制的)但是调用一个额外的处理程序 - 在我的应用程序中发出警告跟踪池是落后的 - 这给了我非常明确的信息, TPE拒绝创建新线程,即使队列中有数千个条目。我的构造函数如下:
private final ExecutorService s3UploadPool =
new ThreadPoolExecutor(1, 40, 1, TimeUnit.HOURS, unboundedLoggingQueue);
为什么不创建新线程?
答案 0 :(得分:18)
这种线程池的构造根本不会按预期工作。这是由于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;
}
然而,更简单地将corePoolSize
设置为maxPoolSize
并且不要担心这种废话。
答案 1 :(得分:4)
正如@djechlin所提到的,这是ThreadPoolExecutor
定义的(令人惊讶的)行为的一部分。我相信我已经找到了一个关于这种行为的优雅解决方案,我在这里回答:
How to get the ThreadPoolExecutor to increase threads to max before queueing?
基本上,你扩展LinkedBlockingQueue
使queue.offer(...)
总是返回false,如果需要,它会向池中添加一个额外的线程。如果池已经处于最大线程并且它们都忙,则将调用RejectedExecutionHandler
。然后是处理程序将put(...)
放入队列。
在那里查看我的代码。
答案 2 :(得分: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
具有Integer.MAX_VALUE
容量可能永远不会发生。因此,将maximumPoolSize
设置为大于corePoolSize
的值时,没有什么意义。
考虑:在超时到期后,线程池有0个空闲线程,这意味着在创建线程之前会有一些延迟(通常,你总是有corePoolSize
个线程可用)。
更多细节可以在ThreadPoolExecutor的JavaDoc中找到。