具有未绑定队列的线程池执行器不创建新线程

我的无法创建新线程。事实上,我写了一个有点黑客,它将接受任何任务(即它是无限的),但调用一个额外的处理程序 - 在我的应用程序中喷出警告跟踪池落后 - 这给了我非常明确的信息,即使队列中有数千个条目,TPE也拒绝创建新线程。我的构造函数如下所示:ThreadPoolExecutorLinkedBlockingQueue

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

为什么不创建新线程?


答案 1

这篇博客文章中介绍了这个陷阱:

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

如果还需要将最小池大小与最大池大小分离,则必须执行一些扩展编码。我不知道Java库或Apache Commons中存在的解决方案。解决方案是创建一个知道 TPE 的耦合,如果它知道 TPE 没有可用的线程,则将不厌其烦地拒绝任务,然后手动重新排队。在链接的帖子中更详细地介绍了它。最终,您的构造将如下所示:BlockingQueue

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


答案 2

此问题有一个解决方法。请考虑以下实现:

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

通过将 allowCoreThreadTimeOut() 设置为 ,池中的线程在指定的超时(在本例中为 60 秒)后终止。使用此解决方案时,构造函数参数将确定实际中的最大池大小,因为线程池将增长到 ,然后开始向队列中添加作业。池可能永远不会变大,因为在队列已满之前,池不会生成新线程(鉴于 具有容量,这可能永远不会发生)。因此,将值设置为大于 的值几乎没有意义。truecorePoolSizecorePoolSizeLinkedBlockingQueueInteger.MAX_VALUEmaximumPoolSizecorePoolSize

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

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