我的ThreadPoolExecutor
无法创建新线程。事实上,我写了一个有点蹩脚的LinkedBlockingQueue
,它将接受任何任务(即它是无界的),但调用一个额外的处理程序——在我的应用程序中,它会发出池落后的警告跟踪——这给了我非常明确的信息,即TPE拒绝创建新线程,即使队列中有数千个条目。我的构造函数如下:
private final ExecutorService s3UploadPool =
new ThreadPoolExecutor(1, 40, 1, TimeUnit.HOURS, unboundedLoggingQueue);
为什么不创建新的线程?
这篇博文讨论了这个问题:
这个线程池的构造不会像预期的那样工作。这是由于ThreadPoolExecutor中的逻辑,如果无法向队列提供任务,则会添加新线程。在我们的例子中,我们使用了一个无界的LinkedBlockingQueue,在这里我们总是可以向队列提供一个任务。这实际上意味着我们永远不会超过核心池大小并达到最大池大小。
如果您还需要将最小池大小与最大池大小解耦,则必须进行一些扩展编码。我不知道存在于Java库或Apache Commons中的解决方案。解决方案是创建一个耦合的BlockingQueue
,它可以感知TPE,并且如果它知道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
,不用担心这些废话。
有一个解决这个问题的方法。考虑以下实现:
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中找到。
正如@ djhlin所提到的,这是ThreadPoolExecutor
定义行为的一部分(让许多人感到惊讶)。我相信我已经找到了一个比较优雅的解决方案来解决这个问题,我在这里给出了答案:
如何让ThreadPoolExecutor在排队之前将线程增加到最大?
基本上,您扩展LinkedBlockingQueue
,使queue.offer(...)
始终返回false,这将在必要时向池中添加额外的线程。如果线程池已经达到最大线程数,并且它们都很忙,则会调用RejectedExecutionHandler
。它是处理程序,然后将put(...)
放入队列。
查看我的代码