Java线程池
1. 线程池参数
1.1 参数说明
参数名 | 描述 |
---|---|
corePoolSize | 线程池核心线程数最大值 |
maximumPoolSize | 线程池最大线程数大小 |
keepAliveTime | 线程池中,非核心线程空闲的存活时间大小 |
unit | 线程空闲存活时间单位,即keepAliveTime 的时间单位 |
workQueue | 存放任务的阻塞队列 |
threadFactory | 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,方便排查问题 |
handler | 线程池的饱和事件策略(拒绝策略),当任务队列满了且线程数已经超过maximumPoolSize ,就会触发 |
线程池里的线程是在任务提交的时候创建:
- 当前运行线程数小于
corePoolSize
: 创建Worker对象,并指向Thread,保持引用以确保不会被GC回收 - 当前运行线程数大于等于
corePoolSize
:尝试任务入队等待(workQueue
)- 入队成功:等待线程池的调度
- 入队失败:判断线程数是否已经超过
maximumPoolSize
- 是:触发拒绝策略(
handler
) - 否:创建非核心线程处理任务
- 是:触发拒绝策略(
上述流程中创建出来的非核心线程,根据 keepAliveTime
+ unit
判断空闲一段时间之后,会被销毁,或者运行的任务抛出异常时也会被销毁
PS: 当task抛出异常后,线程池中的这个工作线程不会再执行其他task,但是会创建出新的线程来运行其他task
1.2 源码注释
线程池的类为 java.util.concurrent.ThreadPoolExecutor
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
1.3 拒绝策略
线程池自带了四种拒绝策略,也可以通过实现 RejectedExecutionHandler
接口来自定义拒绝策略 。
- AbortPolicy:抛出一个
RejectedExecutionException
异常(默认的拒绝策略) - CallerRunsPolicy:将执行的任务直接交给调用线程池的所在线程直接执行。比如调用
execute
方法的线程内 - DiscardPolicy:直接静默地丢弃,不会有任何通知。
- DiscardOldestPolicy:丢弃
workQueue
中最老的任务(即最早提交的任务),然后尝试重新执行当前任务。
1.4 阻塞队列
队列名 | 说明 | 使用场景 |
---|---|---|
ArrayBlockingQueue | 有界阻塞队列,用数组实现 FIFO(先进先出) 初始化时需要指定容量大小 利用 ReentrantLock 实现线程安全 | 数据缓存 限流 生产-消费模式 |
LinkedBlockingQueue | 阻塞队列,用链表实现 是否有界取决于是否设置容量 默认容量为 Integer.MAX_VALUE 一般视为无界队列 | 数据缓存 生产-消费模式 |
DelayQueue | 任务定时周期的延迟执行的无界队列 元素必须实现 Delayed 接口延迟期满时才能从队列中提取元素 | 任务延迟执行 |
PriorityBlockingQueue | 具有优先级的无界阻塞队列 每次出队都返回优先级最高的元素 | 异步处理 任务调度 |
SynchronousQueue | 无缓冲的阻塞队列,不储存元素,入队和出队一起执行 不需要入队等待的场景,详见下面 newCachedThreadPool 的应用 | 线程间通信 多任务非等待处理 |
2. 四种常用线程池介绍
四种常用线程池分别为:
- newFixedThreadPool:固定数目线程的线程池
- newCachedThreadPool:可缓存线程的线程池
- newSingleThreadExecutor:单线程的线程池
- newScheduledThreadPool:定时及周期执行的线程池
2.1 参数说明
四个线程池均支持自定义 threadFactory
拒绝策略默认为RejectedExecutionHandler defaultHandler = new AbortPolicy()
corePoolSize | maximumPoolSize | keepAliveTime | unit | workQueue | ||
---|---|---|---|---|---|---|
newFixedThreadPool | 自定义 | 同corePoolSize | 0 | NANOSECONDS | LinkedBlockingQueue | |
newCachedThreadPool | 0 | Integer.MAX_VALUE | 60 | SECONDS | SynchronousQueue | |
newSingleThreadExecutor | 1 | 1 | 0 | MILLISECONDS | LinkedBlockingQueue | |
newScheduledThreadPool | 自定义 | Integer.MAX_VALUE | 0 | NANOSECONDS | DelayedWorkQueue |
2.2 应用场景
- newFixedThreadPool:线程数一般根据CPU核数设置,用于处理CPU密集型任务,保证在CPU在长期被工作线程使用的场景下,不过多分配线程。
- newCachedThreadPool:任务提交速度大于执行速度时,又不期望进入等待。每次提交直接创建线程去执行,空闲60s后销毁,保证线程池在空闲时不占用资源。
- newSingleThreadExecutor:任务串行执行,由于目前大部分时候应用都是多节点部署,真要串行还得配合其他中间件。
- newScheduledThreadPool :延迟执行任务,并且限制使用线程数的场景。
2.3 注意事项
- 不建议使用默认线程池,需要时请根据自己的需求,自行调整参数;
- 项目内避免不做区分的混用线程池,特别是核心任务,避免核心任务被次要任务拖垮;
- 使用线程池请自定义命名,方便问题排查;
- 线程数请做限制,避免意外情况任务过多导致OOM,反例
newCachedThreadPool
。无界队列同理 - 线程池使用完记得关闭