1. 执行原理/核心参数
1.1 核心参数
核心参数
- corePoolSize 核心线程数目
- maximumPooISize 最大线程数目 =(核心线程+救急线程的最大数目)
- keepAliveTime 生存时间- 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
- unit 时间单位-救急线程的生存时间单位,如秒、毫秒等
- workQueue- 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
- threadFactory 线程工厂 - 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等
- handler 拒绝策略- 当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略
1.2 执行原理
2. 常见的阻塞队列
workQueue - 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
- ArrayBlockingQueue: 基于数组结构的有界阻塞队列,FIFO。
- LinkedBlockingQueue:基于链表结构的有界阻塞队列,FIFO。
- DelayedWorkQueue :是一个优徒级队列,它可以保证每次出队的任务都是当前队列中执行时间最靠前的
- SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作。
ArrayBlockingQueue的LinkedBlockingQueue区别
LinkedBlockingQueue | ArrayBlockingQueue |
---|---|
默认无界,支持有界 | 强制有界 |
底层是链表 | 底层是数组 |
是懒惰的,创建节点的时候添加数据 | 提前初始化 Node 数组 |
入队会生成新 Node | Node需要是提前创建好的 |
两把锁(头尾) | 一把锁 |
两把锁出入分离,性能更高。数组出入共享一把锁,性能较低。
LinkedBlockingQueue实际开发中最好有一个默认值
3. 确定核心线程数
N为CUP核数
3.1 高并发、任务执行时间短
N+1
3.2 并发不高、任务执行时间长
IO密集型任务(2N+1):
文件读写、DB读写、网络请求等
CPU密集型(N+1):
计算型代码(算法多)、Bitmap转换、Gson转换等
3.3 并发高、业务执行时间长
解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考3.2
4. 线程池种类
4.1 固定线程数
场景:适用于任务量已知,相对耗时的任务
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
- 核心线程数与最大线程数一样,没有救急线程
- 阻塞队列是LinkedBlockingQueue,最大容量为IntegerMAX_VALUE
4.2 单线程化
场景:适用于按照顺序执行的任务
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO)执行
- 核心线程数和最大线程数都是1
- 阻塞队列是LinkedBlockingQueue,最大容量为Integer.MAX_VALUE
4.3 可缓存
场景:适合任务数比较密集,但每个任务执行时间较短的情况
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
- 核心线程数为0
- 最大线程数是Integer.MAX_VALUE
- 阻塞队列为SynchronousQueue:不存储元素的阻塞队列,每个插入操作都必须等待一个移出操作。
4.4 延迟、周期执行
场景:可以执行延迟任务的线程池,支持定时及周期性任务执行
5. 不建议用Executors创建线程池
参考阿里开发手册《Java开发手册-嵩山版》
OOM(内存溢出)