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

时间:2013-02-22 20:08:54

标签: java threadpoolexecutor

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

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

6 个答案:

答案 0 :(得分:8)

Javadocs for ThreadPoolExecutor州:

任何BlockingQueue都可用于转移和保留提交的任务。此队列的使用与池大小调整相互作用:

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

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

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

答案 1 :(得分:6)

整体情况如下:

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

提交了10个任务。其中

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

    详细了解ThreadPoolExecutor观看here的动态。该文件的重点如下:

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

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

答案 2 :(得分:1)

我认为另一种方法是根据队列中等待的任务数设置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);
                    }
                }
            }
        }

答案 3 :(得分:0)

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

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

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

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

答案 4 :(得分:0)

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

答案 5 :(得分:-4)

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