线程池主要有以下三种:
SingleThreadExecutor
FixedThreadPool
CachedThreadPool
SingleThreadExecutor
只有一个线程的线程池, 核心线程数为 1, 最大线程数为 1,线程存活时间 0(反正也用不到…),阻塞队列使用的 LinkedBlockingQueue(一个 FIFO 队列)
FixedThreadPool
线程数与核心线程数相同,在构造时指定,线程存活时间 0(反正也用不到…),阻塞队列使用的 LinkedBlockingQueue(一个 FIFO 队列)
CachedThreadPool
核心线程数 0, 最大线程数 2^31 -1 , 存活时间 60s,阻塞队列使用的是 SynchronousQueue(不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则移除操作一直处于阻塞状态;通常吞吐量高于 LinkedBlockingQueue)
通过观察可以看出,他们仨都调用了这个玩意
核心线程数,当线程池中线程数量小于此数值时,存活时间不会生效,即使线程一直闲置(idle),也不会 go die
最大线程数,线程池中允许存在的最大线程数量
存活时间,当线程池中线程数量超过核心线程数,如果有线程处于 idle 状态,超过此 存活时间,就会 狗带
时间单位,通常使用 秒 seconds
阻塞队列,当调用 execute 或者 submit 提交(执行)任务时,如果核心线程数已满,阻塞队列未满,则将 task 丢到阻塞队列中
阻塞队列主要有四种选择:
ArrayBlockingQueue 基于数组的阻塞队列
LinkedBlockingQueue 基于链表的阻塞队列
SynchronousQueue 不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态
PriorityBlockingQueue 具有优先级的无限阻塞队列
RejectedExecutionHandler 拒绝策略,当 阻塞队列,线程池(线程池中线程达到最大线程数时)都满了,将会采取的策略
AbortPolicy 直接抛出异常
CallerRunsPolicy 只用调用者所在线程来运行任务
DiscardOldersPolicy 丢弃队列里最近的一个任务,并执行当前任务
DiscardPolicy 不处理,直接丢掉
execute() 方法的执行过程:
合理配置线程池:
1、CPU 密集型任务,配置 N(CPU数量)+1 个线程数
2、IO 密集型任务,配置 2N + 1 个线程数
加一的原因:
即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保CPU的时钟周期不会被浪费。
参考资料:
1。《Java 并发编程艺术》
2、Java并发核心基础——线程池使用及底层实现机制详解
https://blog.csdn.net/zhangliangzi/article/details/52389766
3、java线程池大小为何会大多被设置成CPU核心数+1?
https://www.zhihu.com/question/38128980
4、Java几种线程池的分析和使用
https://zhuanlan.zhihu.com/p/22882522