项目中使用JDK线程池,应该关注什么?

  • 背景
  • 介绍
  • 实践
    • 创建线程池必须要自定义名字
    • 线程池要考虑业务隔离
    • 谨慎使用默认连接池
    • 不要忘记线程上下文传递
    • 了解模型,合理设置参数
    • 选择合适的异常处理策略
    • 线程池状态需要监控
    • 线程池的动态配置

背景

前两天某项目在生产环境出了点问题,虽然没有定位到具体的原因,倒是暴露了一个线程池配置不恰当的问题。该配置设置了一个比较大的队列长度,导致线程池维持在一个较小的线程数量,业务请求出现明显延迟。

所以这里分享一下,线程池在实际应用中需要注意的点。

介绍

JDK是自带一个线程池实现的,大家要用线程池的话,大概率也是会选择这个。虽然线程池在面试八股文出现概率是挺高的,很多同学谈起原理,源码头头是道,但从开发,运维角度来看,是没有很好落到项目上的。

从原理上看,线程池和连接池都是类似的对象池化技术,只是线程本身就是执行单元,不会从线程池取一个线程出去用。关于大厂的实践,我觉得这个文章是相当全面的了,可以阅读一下Java线程池实现原理及其在美团业务中的实践

实践

建议可以在项目落地,或者把相关实践封装一层给项目方便使用,可以考虑的有:

创建线程池必须要自定义名字

其实不止是线程池,就算普通创建线程的方式,都是要指定一个名字的,好处就是在分析的时候,很容易区分不同的线程或线程池。

线程池要考虑业务隔离

虽然同一个进程内不同线程之间的隔离性不能很好的控制,例如不能限制内存。但不同业务采用不同线程池,也是可以防范一些突发情况的,不至于出现某个业务把线程都占了,影响其他业务的情况。

谨慎使用默认连接池

现在的框架都很强大,例如Springboot,会给你一些默认设置,实践中要考虑这些默认设置是否合理。常见的有@Schedule定时任务,@Async异步,并发流式写法。还有一些异步io库可能也有(例如lettuce用的netty就是基于cpu计算线程数的,在一些高配置主机上可能线程数非常多)。

不要忘记线程上下文传递

对于任务是多阶段异步的场景,线程切换的时候把线程上下文信息传递过去,才有可能在日志或者调用链上把信息串起来,方便后续分析。

了解模型,合理设置参数

很多规范都说过,不要使用JDK提供的那几个默认实现,还是每个参数都要自己去控制,最重要的就是最小最大线程数,队列长度等,要特别注意的是线程池是队列满了以后才会对线程数量扩容。比较常见的错误配置是采用过大的线程数或者队列长度过长。一般规则是根据业务类型来,偏向cpu资源的线程数要小(根据cpu核数来),偏向io资源的线程数要大(一般也就几十到一两百)。至于队列,一般就是几十到一两百,太大容易等待太久。

选择合适的异常处理策略

类似连接池里边的超时等待,拒绝策略也是一种保护措施,会在线程和队列都打满的情况下提交任务时触发。目前的几个默认策略有拒绝,丢弃自身,丢弃旧任务,采用提交线程执行等等,需要根据情况选择合适的策略。另外任务执行报错这种情况(特别是非预期)也是需要验证有没有合适的处理。

线程池状态需要监控

作为一个关键组件,监控是有必要的。像线程池的话,常见的指标有,最小最大线程数,当前线程数,历史最高线程,队列长度,执行任务数,执行任务时长,平均时长(包括百分比指标)等等,并提供访问手段,例如定时输出日志,或者提供接口给监控平台访问。

线程池的动态配置

这个是高级特性了,在上面美团的文章有提供思路。就算没有动态配置,也可以在首次配置经验值,后续根据监控上的统计信息来指导变更。

原文始发于微信公众号(程序员的胡思乱想):项目中使用JDK线程池,应该关注什么?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/22597.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!