ThreadPoolExecutor:任务排队等待但未提交

我们有一个场景,提交给ThreadPoolExecutor的任务是长期运行的。 当线程池启动时,我们启动它,核心池大小= 5,最大池大小= 20,队列大小为10.在我们的应用程序中,大约有10个任务被提交。 大多数情况下,这些任务运行几分钟/小时然后完成。 然而,有一种情况是所有5个任务都被I / O挂起。 结果我的核心池大小达到了最大值,但我的Threadpoolexecutor队列未满。 所以额外的5个任务永远不会有机会运行。 请建议我们如何处理这种情况? 在这种情况下,有一个更小的队列更好的选择吗? 初始化threadPool时最佳队列大小是多少?

另外关于被绞死的任务,我们有什么方法可以从Threadpool中拔出线程? 在这种情况下,至少其他任务将有机会运行。

总体情况是这样的:

 core pool size = 5, max pool size = 20 and queue size of 10 

提交了10个任务。 其中有

  1. 5 I / O挂起的任务=>核心池大小的所有线程都被占用。 因此没有闲置的线程。
  2. 5任务仍然存在。 这5个线程入队排队,因为没有空闲线程,队列可以容纳10个任务。 在队列已满或核心池中的任何线程空闲之前,这些排队的任务将不会执行。

因此,您的程序被绞死。

要了解更多有关ThrePoolExecutor动态的信息, ThrePoolExecutor 点击此处 。 该文件的重点如下:

  • 如果运行的corePoolSize线程少于corePoolSize,则Executor总是更喜欢添加新线程而不是排队。
  • 如果corePoolSize或更multithreading正在运行,则Executor总是更喜欢排队请求而不是添加新线程。
  • 如果请求无法排队,则会创建一个新线程,除非它超过maximumPoolSize,在这种情况下,该任务将被拒绝。

编辑
如果您希望增加核心池大小,则可以使用setCorePoolSize(int corePoolSize) 。 如果增加corepoolsize ,则会根据需要启动新线程以执行任何排队任务。

ThreadPoolExecutor的Javadocs声明:

任何BlockingQueue都可用于传输和保存提交的任务。 此队列的使用与池大小调整交互:

  • 如果运行的corePoolSize线程少于corePoolSize,则Executor总是更喜欢添加新线程而不是排队。
  • 如果corePoolSize或更multithreading正在运行,则Executor总是更喜欢排队请求而不是添加新线程。
  • 如果请求无法排队,则会创建一个新线程,除非它超过maximumPoolSize,在这种情况下,该任务将被拒绝。

除非你在5个线程“挂起”后超过你的队列大小,否则你不会获得更多的线程。

真正的答案是:解决导致线程挂起的问题。 否则你将不得不实现一些方案,该方案使用submit()返回的Future来取消线程,如果它们运行的​​时间太长。

我认为另一种方法是根据队列中等待的任务数设置corePoolSize。 可以使用setCorePoolSize控制corePoolSize。 示例监视器线程可以控制threadPoolExecutor。 您还可以改进此监视器以调整并行度。

  public class ExecutorMonitor extends Thread{ ThreadPoolExecutor executor = null; int initialCorePoolSize; public ExecutorMonitor(ThreadPoolExecutor executor) { this.executor = executor; this.initialCorePoolSize = executor.getCorePoolSize(); } @Override public void run() { while (true) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } if (executor.getQueue().size() > 0) { if(executor.getActiveCount() < executor.getMaximumPoolSize()) executor.setCorePoolSize(executor.getCorePoolSize() + 1); } if (executor.getQueue().size() == 0) { if(executor.getCorePoolSize() > initialCorePoolSize) executor.setCorePoolSize(executor.getCorePoolSize() -1); } } } } 

jdk中的“变量”大小的线程池function有点棘手。 基本上,由于您概述的原因,您使用的设置将无法正常工作。 当我想要一个可变大小的线程池时,我通常使用这个设置:

  ThreadPoolExecutor executor = new ThreadPoolExecutor(maxPoolSize, maxPoolSize, DEFAULT_THREAD_TIMEOUT, DEFAULT_THREAD_TIMEOUT_UNIT, new LinkedBlockingQueue(), threadFactory); executor.allowCoreThreadTimeOut(true); 

这将创建一个可变大小的线程池,它将在1和maxPoolSize线程之间变化。 由于核心大小与最大大小相同,因此池总是倾向于将队列添加到队列中,因此在线程数最大化之前,您永远不会备份队列。

更新:至于您的挂起任务问题,如果任务的长度有一个上限,您可以让一个单独的经理跟踪未完成的Future,如果超过最大运行时间则取消它们。 当然,这假设你的任务是可以中断的。

基本问题是用户真正获得了具有无限队列方案的CORE_POOL_SIZE线程。 因此,如果核心池中有5个线程是他可以使用的全部线程,则max size无助于提供帮助。 虽然在所有情况下都建议减少线程执行的时间,但在生产场景中,我们通常无法控制第三方服务的行为方式,因此解决方案是将核心池大小增加到等于最大池大小或限制队列大小。

我想最好的解决方案是覆盖ThreadPoolExecutor的execute方法并引入其中的更改。