java 具有无界队列的 ThreadPoolExecutor 不创建新线程
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15485840/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ThreadPoolExecutor with unbounded queue not creating new threads
提问by djechlin
My ThreadPoolExecutor
is failing to create new threads. In fact I wrote a somewhat hacky LinkedBlockingQueue
that will accept any task (i.e. it is unbounded) but call an additional handler - which in my application spews warning trace that the pool is behind - which gives me very explicit information that the TPE is refusing to create new threads even though the queue has thousands of entries in it. My constructor is as follows:
我ThreadPoolExecutor
的无法创建新线程。事实上,我写了一个有点hacky的东西LinkedBlockingQueue
,它会接受任何任务(即它是无界的)但调用一个额外的处理程序——在我的应用程序中发出警告跟踪池落后——这给了我非常明确的信息,TPE拒绝创建新线程,即使队列中有数千个条目。我的构造函数如下:
private final ExecutorService s3UploadPool =
new ThreadPoolExecutor(1, 40, 1, TimeUnit.HOURS, unboundedLoggingQueue);
Why is it not creating new threads?
为什么不创建新线程?
回答by djechlin
This gotcha is covered in this blog post:
This construction of thread pool will simply not work as expected. This is due to the logic within the ThreadPoolExecutor where new threads are added if there is a failureto offer a task to the queue. In our case, we use an unbounded LinkedBlockingQueue, where we can always offer a task to the queue. It effectively means that we will never grow above the core pool size and up to the maximum pool size.
线程池的这种构造不会按预期工作。这是由于 ThreadPoolExecutor 中的逻辑,如果向队列提供任务失败,则会添加新线程。在我们的例子中,我们使用一个无界的 LinkedBlockingQueue,我们总是可以向队列提供一个任务。这实际上意味着我们永远不会超过核心池大小并达到最大池大小。
If you also need to decouple the minimum from maximum pool sizes, you will have to do some extended coding. I am not aware of a solution that exists in the Java libraries or Apache Commons. The solution is to create a coupled BlockingQueue
that is aware of the TPE, and will go out of its way to reject a task if it knows the TPE has no threads available, then manually requeue. It is covered in more detail in linked post. Ultimately your construction will look like:
如果您还需要将最小池大小与最大池大小分离,则必须进行一些扩展编码。我不知道 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;
}
However more simply set corePoolSize
to maxPoolSize
and don't worry about this nonsense.
然而,更简单地设置corePoolSize
为maxPoolSize
并且不要担心这个废话。
回答by Gray
As mentioned by @djechlin, this is part of the (surprising to many) defined behavior of the ThreadPoolExecutor
. I believe I've found a somewhat elegant solution around this behavior that I show in my answer here:
正如@djechlin 所提到的,这是ThreadPoolExecutor
. 我相信我已经找到了一个围绕这种行为的优雅解决方案,我在这里的回答中显示了这一点:
How to get the ThreadPoolExecutor to increase threads to max before queueing?
Basically you extend LinkedBlockingQueue
to have it always return false for queue.offer(...)
which will add an additional threads to the pool, if necessary. If the pool is already at max threads and they all are busy, the RejectedExecutionHandler
will be called. It is the handler which then does the put(...)
into the queue.
基本上,您可以扩展LinkedBlockingQueue
以使其始终返回 false,queue.offer(...)
这将在必要时向池中添加额外的线程。如果池已经达到最大线程并且它们都忙,RejectedExecutionHandler
则将调用 。然后是处理程序put(...)
进入队列。
See my code there.
在那里查看我的代码。
回答by matsev
There is a workaround to this problem. Consider the following implementation:
此问题有一个解决方法。考虑以下实现:
int corePoolSize = 40;
int maximumPoolSize = 40;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
threadPoolExecutor.allowCoreThreadTimeOut(true);
By setting the allowCoreThreadTimeOut()to true
, the threads in the pool are allowed to terminate after the specified timeout (60 seconds in this example). With this solution, it is the corePoolSize
constructor argument that determines the maximum pool size in practice, because the thread pool will grow up to the corePoolSize
, and then start adding jobs to the queue. It is likely that the pool may never grow bigger than that, because the pool will not spawn new threads until the queue is full (which, given that the LinkedBlockingQueue
has an Integer.MAX_VALUE
capacity may never happen). Consequently, there is little point in setting maximumPoolSize
to a larger value than corePoolSize
.
通过将allowCoreThreadTimeOut()设置为true
,允许池中的线程在指定的超时(在本例中为 60 秒)后终止。有了这个解决方案,实际corePoolSize
中决定最大池大小的是构造函数参数,因为线程池会增长到corePoolSize
,然后开始向队列中添加作业。它是可能的,直到队列已满(其中,由于该池可能永远不会比大,因为池不会产生新的线程LinkedBlockingQueue
有Integer.MAX_VALUE
能力可能永远不会发生)。因此,设置maximumPoolSize
为大于 的值几乎没有意义corePoolSize
。
Consideration: The thread pool have 0 idle threads after the timeout has expired, which means that there will be some latency before the threads are created (normally, you would always have corePoolSize
threads available).
注意事项:超时到期后,线程池有 0 个空闲线程,这意味着在创建线程之前会有一些延迟(通常,您将始终有corePoolSize
可用线程)。
More details can be found in the JavaDoc of ThreadPoolExecutor.
更多细节可以在ThreadPoolExecutor的 JavaDoc 中找到。