文章目录
- 前言
- 一、线程池的几种任务队列
- 1.ArrayBlockingQueue
- 2.LinkedBlockingQueue
- 3.DelayedWorkQueue
- 4.SynchronousQueue
- 二、如何确定核心线程数?
- 三、线程池的种类有哪些?
- 1.newFixedThreadPool
- 2.newSingleThreadExecutor
- 3.newCacheThreadPool
- 4.newScheduledThreadPool
- 四、为什么不建议使用Executor创建线程池?
- 总结
前言
这篇博客记录一下关于线程池的几种任务队列,一般常用的任务队列是哪种,以及怎么确定线程池的线程数,还有几种常用的线程池创建方式。
一、线程池的几种任务队列
常用的就是ArrayBlockingQueue和LinkedBlockingQueue
1.ArrayBlockingQueue
- ArrayBlockingQueue是一种有界的数组型任务队列,基于FIFO(先进先出的任务队列)
如图看ArrayBlockingQueue的构造函数,参数中有一个容量的参数是必传的,因此它是一个有界的任务队列。
2.LinkedBlockingQueue
- LinkedBlockingQueue是一个无解的底层是一个单链表的任务队列,也是基于FIFO(先进先出的任务队列)
它默认是无界的,是Integer的最大值。
3.DelayedWorkQueue
- 是一个优先级队列,它可以保证每次出队的任务都是当前队列中执行时间最靠前的
4.SynchronousQueue
- 不存储元素的阻塞队列,每个插入操作都必须等待下一个移出操作
二、如何确定核心线程数?
- 1.高并发、任务执行时间短,采用(CPU核数+1)原则确定核心线程数,减少线程上下文的切换
- 2.如果是并发不高、任务执行时间长,若是IO密集型任务(CPU核数*2 + 1);若是计算密集型任务(CPU核数+1)
- 3.并发高、且业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,参考以上原则进行设置
三、线程池的种类有哪些?
1.newFixedThreadPool
- 创建一个定长的线程池,可控制最大并发数,超出的线程会在队列中等待
看源码可以知道,传入的线程数就是最大线程数和核心线程数,救急线程的活跃时间是0毫秒,使用的任务队列是LinkedBlockingQueue,而且并没有传入任务队列的容量,意味着容量是Integer.MAX_VALUE,这是一个风险
2.newSingleThreadExecutor
- 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO)执行
newSingleThreadExecutor的核心线程数和最大线程数都是1,救急线程的存活时间为0毫秒,任务队列是LinkedBlockingQueue
3.newCacheThreadPool
- 创建一个可缓存的线程池,如果线程池长度超过,处理需要,可灵活回收空闲线程,若无可回收,则新建线程
newCacheThreadPool核心线程数为0,最大线程数为Integer.MAX_VALUE,救急线程存储时间为60S,任务队列是SynchronousQueue,不存储任务的队列
4.newScheduledThreadPool
- 可以执行延迟任务的线程池,支持定时及周期性任务执行
newScheduledThreadPool核心线程数需要定义,最大线程数是Integer.MAX_VALUE,救急线程存活时间是0ms,任务队列是DelayedWorkQueue
四、为什么不建议使用Executor创建线程池?
- 1.线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的人更加明确线程池的运行规则,避免资源耗尽的风险
- 2.FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM(内存溢出)
- 3.CachedThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM
总结
线程池有关知识点先告一段落,下面我会继续总结并发编程中的安全相关问题,比如Synchronized锁,乐观锁,悲观锁,以及死锁等问题。