线程池可以帮助我们省去创建、回收线程所带来的资源消耗,这也是目前使用线程池的主要原因:
下面我根据线上业务依次分析常见的几种线程池并给出个人理解:
特点:只有一个线程,阻塞队列使用 LinkedBlockingQueue,链表无界。优点在于保证顺序
我个人不推荐使用这种线程池,首先它的应用场景很少,单个线程也无法发挥多核的优势。只适合用于一些边缘业务,不重要也不考虑稳定性的场景可以通过这个线程池慢慢跑
如果只是为了保证顺序,我更倾向通过消息队列这种方式,而不是线程池。首先通过消息队列的好处在于流量可控,这点非常重要,哪怕只通过一个线程每次消费一条消息,消息挤压在 MQ 都比挤压在无界队列好。一般线上强烈不建议使用无界队列,一旦涉及到上线,重启你完全无法预知阻塞队列中还有多少未执行的任务,一旦重启丢了就是丢了,完全不能找回
重要的事情再说一遍,线上强烈不建议使用无界队列作为线程池的阻塞队列,因为无法预知消息的积压量,而且很有可能造成内存泄漏
和 SingleThreadPool 类似,唯一区别在于可以多线程并发。因为使用无界队列,还是不建议使用
这种线程池主要用于应对流量不均匀的场景,对于存在业务高峰期的系统可以尝试使用,不过需要防止高并发流量一下次创建太多线程,虽然可能系统本身没有挂,但是多线程并发的流量一下子把其它中间件打挂了,比如 MySQL
之前博客中提到可以做优先级,主要用于定时任务之类。目前我线上用到较少,不过应该可以考虑使用,这里优先级是通过阻塞队列实现,可以将一些重要的任务交给该线程池执行。至于定时器,线上很少会通过线程池做定时,一般公司会有专业的定时任务中间件,如 XXLJOB 等等
单线程的线程池线上用的较少,因为效率还是比较低的,无法发挥多核的优势,不如 ScheduledThreadPool
常见的五种线程池介绍完了,其实一般线上更推荐大家自己写参数,而不是套模板,根据自己业务量配置出的线程池肯定比这种统一出来的线程池好使,而且更契合实际业务,不过有几点共识可以遵守:
最后我再简单介绍目前我的应用常用的几种线程池配置:
守护线程池:主要执行一些清理及不关键的业务场景
普通线程池:绝大多数业务场景都可以使用普通线程池
无队列线程池:主要用于处理一些对性能有要求的场景,比如消费 MQ
至于拒绝策略,守护线程池可以设置为 DiscardPolicy,因为其中它的工作不重要,适当可以丢弃。普通线程池和无队列线程池都采用CallerRunsPolicy,线上的 MQ 无论如何都不能丢弃。只能通过当前线程执行,这样的结果可能导致 mq 消费过慢,此时可以通过扩容或者优化逻辑的方式防止 mq 积压