>>分享Java编程技术,对《Java面向对象编程》等书籍提供技术支持 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 25648 个阅读者 刷新本主题
 * 贴子主题:  JDK线程池中调整coreSize无效的问题分析及处理 回复文章 点赞(0)  收藏  
作者:javathinker    发表时间:2016-10-18 18:00:18     消息  查看  搜索  好友  复制  引用

原文出处: i flym  https://www.iflym.com/index.php/code/201510080001.html

近期在ETL项目中,增加一个用于监控队列数和当前线程数之间的关系,并动态调节线程池大小的一个功能。其作用机制即是指当发现队列中任务太多时,能够增大线程数,以达到使用更多的线程来运行任务的目的。相应的伪代码如下所示:

extThreadPoolExecutor.setCorePoolSize(newCorePoolSize);
extThreadPoolExecutor.prestartCoreThread();

但是在后面通过监控发现以下的信息

队列总数:1670,核心线程数:500,活跃线程数:200

即相应的核心线程数是已经作了相应的调整,但是活跃线程数,却始终没有上升。没有达到调节线程数的目的。
从理论上说,如果增大了核心线程数,那么线程数会在新任务时,会自动创建新的线程来进行相应的任务,并且我们通过手动启动核心线程来强制运行任务,因此也不会出现线程并没有创建的问题(详细可以查看prestartCoreThread的作用)。

重新查看相应的源代码,发现此线程池在设计层面是这样进行定义的:

corePoolSize=maximumPoolSize

即在这次调整中,并没有调整最大线程池大小,即导致core >max的现象发生。官方文档中并没有说这种情况下会出现什么情况,因此只有实际去查看源码。跟踪maximusPoolSize在类ThreadPoolExecutor中的使用,最终可以看到在方法getTask在如下使用,代码如下所示:

private Runnable getTask() {
  boolean timedOut = false; // Did the last poll() time out?

  for (;;) {
    int c = ctl.get();
    int rs = runStateOf(c);
    //......
    int wc = workerCountOf(c);

    // Are workers subject to culling?
    boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

    //这里如果当前线程数工作线程大于max,则直接返回null
    if ((wc > maximumPoolSize || (timed && timedOut))
        && (wc > 1 || workQueue.isEmpty())) {
        if (compareAndDecrementWorkerCount(c))
            return null;
        continue;
    }
    //......
}

在这里因为,当前工作线程数肯定大于max(因为使用了prestartCoreThread),会返回null,而此调用的调用者为 runWorker,即实际上为刚刚创建好的线程准备去拿任务,并没有拿到,因此会正常地退出(因为,在线程池中设置了allowCoreThreadTimeOut).整个流程如下所示:

prestartCoreThread工作线程数+1
运行刚新建的线程worker
worker获取task
没有拿到task
worker正常结束
清理worker,工作线程数-1

最终的效果就是刚启动的线程马上就结束了,导致工作线程始终不能增加,即不能创建新的线程。

找到了上面的问题,解决方法也很简单,即同时调节相应的maximusPoolSize,即可。在调节代码中按照以下的步骤进行处理即可

extThreadPoolExecutor.setMaximusPoolSize(newCorePoolSize);
extThreadPoolExecutor.setCorePoolSize(newCorePoolSize);

上面代码,先设置max的目的在于保证新创建的核心线程能够获取到任务或者在获取任务的步骤当中,而不是直接退出.

附:在当前的线程池当中,我们为什么将corePoolSize设置成maximum一样的原因也在于需要手动控制线程数的大小,同时设置allowCoreTimeOut以保证即使core设置成很大也没有关系,在空闲时core线程也会终结。也可以理解为与core设置为0,max设置为相应值一样的效果。因为,在应用中并不是通过executor.addTask来添加任务(而是通过直接操纵queue来添加任务),因此在应用中在queue.add(task)时,通过prestartCoreThread来手动控制新线程的创建过程。


程序猿的技术大观园:www.javathinker.net
  Java面向对象编程-->Java常用类(上)
  JavaWeb开发-->Web运作原理(Ⅱ)
  JSP与Hibernate开发-->持久化层的映射类型
  Java网络编程-->XML数据处理
  精通Spring-->组合(Composition)API
  Vue3开发-->创建综合购物网站应用
  java的三种随机数生成方式
  解密Java类文件的数据结构
  ThreadLocal的内存泄露的原因分析以及如何避免
  Java虚拟机(JVM)参数配置说明
  Java关键字final、static使用总结
  BST 二叉搜索树
  Java是如何实现自己的SPI机制的?
  volatile 实现原理
  小数在内存中是如何存储的?
  Eclipse使用指南:常用视图(View) 的用法
  Java入门实用代码:使用 Enumeration 遍历 HashTable
  JAVA设计模式之备忘录模式原理与用法详解
  Eclipse中怎么一键去除java项目中的信息打印语句
  Java中的main()方法详解
  Java线程实现龟兔赛跑
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。