当前位置: 首页 > article >正文

线程池动态设置线程大小踩坑

在配置线程池核心线程数大小和最大线程数大小后,如果调用线程池setCorePoolSize方法来调整线程池中核心线程的大小,需要特别注意,可能踩坑,说不定增加了线程让你的程序性能更差。

ThreadPoolExecutor有提供一个动态变更线程池核心线程数大小的方法setCorePoolSize,如果我们使用次方法来改变线程池的线程数确实可以实现想要的效果,但是会有一些意想不到的结果。比如,出现下图的情况。

先简单介绍一下背景,我先创建了一个核心线程数和最大线程数都为1的线程池,然后通过ThreadPoolExecutor.setCorePoolSize()方法设置核心线程数为5,接着继续往线程池中丢任务,我看通过截图可以看到,这些线程在执行完一次任务后就直接销毁了,并不是想像中的执行完一个任务后持续的执行队列中的任务或者是阻塞等待新任务的到来。如果一直持续这样,系统的性能其实是会更差的,因为一直在创建线程销毁线程。

但是,如果我是创建一个核心线程数和最大线程数都为5的线程池,然后通过ThreadPoolExecutor.setCorePoolSize()方法设置核心线程数为1或者2。如果我持续往里面丢任务,核心线程数也是保持在我设定的值。

小结

如果此时是将线程数调小没有任何问题,线程在处理完任务以及队列中的任务后会阻塞在队首等待任务到来被唤醒。

如果此时是将线程数调大就需要注意,当有任务到来时,如果当前核心线程数小于配置的,则会创建新的线程,但是它执行完一次任务后就会被销毁,即使队列中还有任务等待执行,他也会立马被销毁。

问题排查

出现问题第一反应就是线程在执行完当前任务后,获取任务的时候出了问题,于是在java.util.concurrent.ThreadPoolExecutor#getTask方法中获取队列任务的地方打断点排查是否是获取任务出现问题。初步怀疑是这里timed判断有问题导致的,结果断点都没有到这里。

boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

if ((wc > maximumPoolSize || (timed && timedOut))
    && (wc > 1 || workQueue.isEmpty())) {
    if (compareAndDecrementWorkerCount(c))
        return null;
    continue;
}

try {
    Runnable r = timed ?
        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
        workQueue.take();
    if (r != null)
        return r;
    timedOut = true;
} catch (InterruptedException retry) {
    timedOut = false;
}

以为是别的地方有问题,转了一圈回来还是怀疑这个方法,最后仔细看了一遍,发现了问题所在。

出现以上问题的根本原因就是我们只动态修改了核心线程数大小,未同步修改最大线程数大小。每一次任务执行完成后需要调用java.util.concurrent.ThreadPoolExecutor#getTask方法,其中有一段下面的逻辑。由于我们只调整了核心线程数,未同步修改最大线程数,这里就会走到returen null的逻辑,就会退出当前线程。

if ((wc > maximumPoolSize || (timed && timedOut))
    && (wc > 1 || workQueue.isEmpty())) {
    if (compareAndDecrementWorkerCount(c))
        return null;
    continue;
}

总结

在动态修改线程的核心线程数后也需要同步修改最大线程数,否则会导致线程创建后执行一个任务后就被销毁。


http://www.kler.cn/news/315508.html

相关文章:

  • Hadoop的安装和使用
  • 【JavaScript】数据结构之树
  • Qt 学习第十天:小项目:QListWidget的使用
  • 【基于Spring Boot的汽车租赁系统】
  • 【微信小程序】连续拍照功能实现
  • kafka 消息位移提交几种方式:消息重复消息、消息丢失的关键
  • Docker_基础初识
  • 新能源汽车知识点集萃
  • Python办公自动化教程(003):PDF的加密
  • HarmonyOS Next开发----使用XComponent自定义绘制
  • 【乐企-工具篇】有关乐企发票文件生成- OFD和PDF文件生成
  • 四、JVM原理-4.1、JVM介绍
  • vue中 <template> 与 <template lang=“jade“>的对比,哪个性能好
  • 数据结构之希尔排序
  • 轻代码的概念学习笔记
  • http和https的区别及get和post请求的区别
  • Vue3新组件transition(动画过渡)
  • Java API 之集合框架进阶
  • 软件测试面试题(5)——二面(游戏测试)
  • 【PLW003】设备器材云端管理平台v1.0(SpringBoot+Mybatis+NodeJS+MySQL前后端分离)
  • LeetCode题练习与总结:回文链表--234
  • [JavaEE]———进程、进程的数据结构、进程的调度
  • 【优选算法之二分查找】No.5--- 经典二分查找算法
  • Linux之实战命令03:stat应用实例(三十七)
  • 如何使用 maxwell 同步到 redis?
  • 如何在 CentOS 中管理用户、组和服务状态
  • git pull的merge和rebase模式
  • Spring解决循环依赖的原理
  • RuntimeError: Maximum Recursion Depth Exceeded - 递归深度超限的完美解决方案
  • Spring 源码分析