Java教程

关于线上常用的几种线程池配置

本文主要是介绍关于线上常用的几种线程池配置,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

线程池可以帮助我们省去创建、回收线程所带来的资源消耗,这也是目前使用线程池的主要原因:

下面我根据线上业务依次分析常见的几种线程池并给出个人理解:

  • SingleThreadPool:无界队列,核心线程、最大线程都为 1

特点:只有一个线程,阻塞队列使用 LinkedBlockingQueue,链表无界。优点在于保证顺序

我个人不推荐使用这种线程池,首先它的应用场景很少,单个线程也无法发挥多核的优势。只适合用于一些边缘业务,不重要也不考虑稳定性的场景可以通过这个线程池慢慢跑

如果只是为了保证顺序,我更倾向通过消息队列这种方式,而不是线程池。首先通过消息队列的好处在于流量可控,这点非常重要,哪怕只通过一个线程每次消费一条消息,消息挤压在 MQ 都比挤压在无界队列好。一般线上强烈不建议使用无界队列,一旦涉及到上线,重启你完全无法预知阻塞队列中还有多少未执行的任务,一旦重启丢了就是丢了,完全不能找回

重要的事情再说一遍,线上强烈不建议使用无界队列作为线程池的阻塞队列,因为无法预知消息的积压量,而且很有可能造成内存泄漏

  • newFixedThreadPool:无界队列,核心线程、最大线程都为 n

和 SingleThreadPool 类似,唯一区别在于可以多线程并发。因为使用无界队列,还是不建议使用

  • newCachedThreadPool:没有核心线程,工作线程无界

这种线程池主要用于应对流量不均匀的场景,对于存在业务高峰期的系统可以尝试使用,不过需要防止高并发流量一下次创建太多线程,虽然可能系统本身没有挂,但是多线程并发的流量一下子把其它中间件打挂了,比如 MySQL

  • ScheduledThreadPool:有核心线程,最大线程无界,阻塞队列有界,存在优先级维度

之前博客中提到可以做优先级,主要用于定时任务之类。目前我线上用到较少,不过应该可以考虑使用,这里优先级是通过阻塞队列实现,可以将一些重要的任务交给该线程池执行。至于定时器,线上很少会通过线程池做定时,一般公司会有专业的定时任务中间件,如 XXLJOB 等等

  • SingleThreadScheduledPool:和上一种类似,唯一区别在于单线程

单线程的线程池线上用的较少,因为效率还是比较低的,无法发挥多核的优势,不如 ScheduledThreadPool


常见的五种线程池介绍完了,其实一般线上更推荐大家自己写参数,而不是套模板,根据自己业务量配置出的线程池肯定比这种统一出来的线程池好使,而且更契合实际业务,不过有几点共识可以遵守:

  1. 尽量不要用单线程线程池,无法发挥多核优势
  2. 尽量不要使用无界队列,消息可以积压在 kafka 不要积压在无界队列
  3. 核心线程数、最大线程数最好根据业务自行设置
  4. 建议不要只用某一种线程池,多定义几种不同类型的线程池,写在同一个 Service 中,通过 Spring IOC 容器管理对外提供不同服务:

最后我再简单介绍目前我的应用常用的几种线程池配置:

  1. 普通线程池,核心线程数 10、最大线程数 20,阻塞队列有界
  2. 守护线程池,配置同普通线程
  3. 无队列线程池,核心线程数20、最大线程数 40,阻塞队列长度 0

守护线程池:主要执行一些清理及不关键的业务场景
普通线程池:绝大多数业务场景都可以使用普通线程池
无队列线程池:主要用于处理一些对性能有要求的场景,比如消费 MQ

至于拒绝策略,守护线程池可以设置为 DiscardPolicy,因为其中它的工作不重要,适当可以丢弃。普通线程池和无队列线程池都采用CallerRunsPolicy,线上的 MQ 无论如何都不能丢弃。只能通过当前线程执行,这样的结果可能导致 mq 消费过慢,此时可以通过扩容或者优化逻辑的方式防止 mq 积压

这篇关于关于线上常用的几种线程池配置的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!