【踩坑】慎用线程池,导致生产环境假死
创始人
2024-02-08 11:43:08
0

【踩坑】慎用线程池,导致生产环境假死

    • 1.场景
    • 2.问题
    • 3.总结
    • 4.填坑
    • 5.补充,排查问题,java进程压测时不知为何崩溃(killed)
      • 线程和内存

1.场景

  1. 项目里的很多地方的分页查询,原本是复杂的sql,然后我将其优化成查主表,然后在service里组装数据。组装的话,需要查询好几个不同的sql,就想到使用线程池异步去执行,最后一起拼装
  2. 结果毫无疑问的线上环境出问题了,访问很慢,回滚,找原因
  3. 发现是在写线程池的时候有问题

2.问题

  public static MdcThreadPoolExecutor getMdcPool() {return new MdcThreadPoolExecutor(8, 32, 1, TimeUnit.MINUTES,new LinkedBlockingQueue<>(10240),new NamedThreadFactory("MDC_POOL-",false));}

问题1: 一开始应该是打算写工厂类,单例线程池的,后面估计忘了变成了每次都new一个线程池。
问题2:线程数,队列数是否合理?没有尝试压测。试想一下,如果sql都是一些慢sql,300ms左右,那8个核心线程1s能处理 1/0.3*8≈24个任务。
假设某个时刻qps是50,即每秒+50-24,也就是每秒50-24=26个任务进入队列,需要390s约等于6分半钟队列才满,然后增加线程提高处理速度。 换个角度,如果不考虑超时,如果刚刚好10247个任务同时请求,8个核心线程开始工作,10247-8=10239个任务在队列里,最后一个任务需要等10239/24=426s也就是7分钟才能轮到它,当然这是极端情况,但也就意味着有可能会有一些任务在队列中等待过久超时

3.总结

  1. 线程池最好统一管理起来,不要随便new,或者new了用完记得关闭,或者使用jdk提供的ForkJoinPool等框架自带的,或者写单例
  2. 线程池的线程数,队列大小需要考虑,不同的场景(IO密集和CPU密集)应该用不同的线程池。队列太长,可能难以触发增加线程数到maxThread,导致在等待时间过长,超时。队列太小,则需要考虑maxThead大小,避免线程不够用

4.填坑

  1. 将使用到的线程池都集中在一个类中,写懒加载单例
  2. 对于异步查sql用的线程池,用jmeter测试效果,发现core=max=jdbc最大连接数的效果比较好,队列大小则综合考虑 平均查询时间,业务允许等待时间, 线程数来考虑设置
  3. 使用线程池的地方,future获取结果加上了get()超时时间,快速失败,避免阻塞业务线程。这里注意get(time,timeUnit)超时后,主线程抛出异常,但是子线程还是会继续执行

5.补充,排查问题,java进程压测时不知为何崩溃(killed)

一开始还没发现这个问题,在测试环境用jmeter压测的时候,发现程序老崩溃。

  1. 一开始以为是压测,然后内存不够导致的,但是没看到oom的以后的oom日志(jvm启动命令加了HeapDumpAfterFullGC ),而且加大了内存,后面压测的时候jstat -gc [pid] 1000也没看到gc飙升,说明内存是够用的
  2. 然后以为是内存飙升导致被linux给killed了,用dmesg -T|grep java看到kill日志是前几天的
  3. 然后实在没日志看了,看hs_err_pid.log,然后看到说swap内存不够,不是堆内存不够也不是metaspace元空间不够,然后查了资料,发现是因为线程数太多

线程和内存

这个异常问题本质原因是我们创建了太多的线程,而能创建的线程数是有限制的,导致了异常的发生。能创建的线程数的具体计算公式如下:

具体计算公式如下:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads

总结下影响Java线程数量的因素

  • Java虚拟机本身占用的内存:-Xms,-Xmx,因为线程占用的内存是堆外内存(所以上面的问题查看heap并没有内存oom)
  • 线程栈的大小,-Xss
  • 系统自身限制: /proc/sys/kernel/pid_max, /proc/sys/kernel/thread-max, max_user_process(命令ulimit -u查看), /proc/sys/vm/max_map_count。
  • 想增加线程数,在JVM内部可以通过减少最大堆或减少栈容量来实现

从我看到的错误日志errno=12,应该属于内存不足以至于不能创建新线程

相关内容

热门资讯

喜欢穿一身黑的男生性格(喜欢穿... 今天百科达人给各位分享喜欢穿一身黑的男生性格的知识,其中也会对喜欢穿一身黑衣服的男人人好相处吗进行解...
发春是什么意思(思春和发春是什... 本篇文章极速百科给大家谈谈发春是什么意思,以及思春和发春是什么意思对应的知识点,希望对各位有所帮助,...
网络用语zl是什么意思(zl是... 今天给各位分享网络用语zl是什么意思的知识,其中也会对zl是啥意思是什么网络用语进行解释,如果能碰巧...
为什么酷狗音乐自己唱的歌不能下... 本篇文章极速百科小编给大家谈谈为什么酷狗音乐自己唱的歌不能下载到本地?,以及为什么酷狗下载的歌曲不是...
华为下载未安装的文件去哪找(华... 今天百科达人给各位分享华为下载未安装的文件去哪找的知识,其中也会对华为下载未安装的文件去哪找到进行解...
家里可以做假山养金鱼吗(假山能... 今天百科达人给各位分享家里可以做假山养金鱼吗的知识,其中也会对假山能放鱼缸里吗进行解释,如果能碰巧解...
四分五裂是什么生肖什么动物(四... 本篇文章极速百科小编给大家谈谈四分五裂是什么生肖什么动物,以及四分五裂打一生肖是什么对应的知识点,希...
怎么往应用助手里添加应用(应用... 今天百科达人给各位分享怎么往应用助手里添加应用的知识,其中也会对应用助手怎么添加微信进行解释,如果能...
客厅放八骏马摆件可以吗(家里摆... 今天给各位分享客厅放八骏马摆件可以吗的知识,其中也会对家里摆八骏马摆件好吗进行解释,如果能碰巧解决你...
美团联名卡审核成功待激活(美团... 今天百科达人给各位分享美团联名卡审核成功待激活的知识,其中也会对美团联名卡审核未通过进行解释,如果能...