Java线程池底层原理
Java线程池是用来优化多线程的,由于原始线程的创建以及关闭(线程垃圾的回收)是非常消耗资源的的,而线程池是用来优化多个线程反复创建或者关闭的操作的。
线程池的创建
ThreadPoolExecutor pool = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数 推荐为cpu核心数 * 2
10, // 线程存活时间:空闲线程或者设置allowCoreThreadTimeout=true的核心线程
TimeUnit.MINUTES, // 线程存活时间单位
new ArrayBlockingQueue<>(3), // 阻塞队列/任务队列
Executors.defaultThreadFactory(), // 线程工厂,创建线程的方式,可以自定义
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
拒绝策略 | 描述 |
---|---|
AbortPolicy | 拒绝并抛出异常。 |
CallerRunsPolicy | 重试提交当前的任务,即再次调用运行该任务的execute()方法。 |
DiscardOldestPolicy | 抛弃队列头部(最旧)的一个任务,并执行当前任务。 |
DiscardPolicy | 抛弃当前任务。 |
线程池创建任务
pool.execute(new Runnable() {
@Override
public void run() {
方法名(参数);
}
});
execute()方法的源码
public void execute(Runnable command) {
if (command == null) // 先排除异常值,也就是传入了空指针线程变量
throw new NullPointerException();
int c = ctl.get(); // 获取主线程池控制状态
if (workerCountOf(c) < corePoolSize) { // 判断工作中的线程是否小于核心线程
// 尝试添加线程,参数为true表示是否新建线程,因为多线程环境下的并发操作可能导致幻读
// true,表示新增线程时,判断当前线程数是否少于corePoolSize,即会创建核心线程
if (addWorker(command, true))
return;
c = ctl.get(); // 如果没成功,重新获取线程池状态
}
if (isRunning(c) && workQueue.offer(command)) { // 查看线程是否在运行,并且将任务添加到任务队列
int recheck = ctl.get(); // 再次获取线程池状态
if (! isRunning(recheck) && remove(command)) // 二次状态检查,非RUNNING态,从队列移除
reject(command); // 执行拒绝策略
// 如果设置allowCoreThreadTimeOut=true,核心线程就会因空闲全部回收,所以导致工作线程数为0
else if (workerCountOf(recheck) == 0)
// 为什么传null,线程启动后会自动从阻塞队列拉任务执行
// false,表示新增线程时,判断当前线程数是否少于maximumPoolSize,即会创建非核心线程,也就是空闲线程
addWorker(null, false);
}
// 尝试创建空闲线程
else if (!addWorker(command, false))
// 创建失败,执行拒绝策略
reject(command);
}
ctl:主池控制状态
// 定义ctl:主池控制状态,一个AtomicInteger,低28位用来表示线程数,高4位用来表示线程池状态。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// AtomicInteger 用于原子递增计数器等应用程序,不能用作 Integer 的替代品。
private volatile int value;
public AtomicInteger(int initialValue) {
value = initialValue;
}
线程添加任务流程
简单记
- 当线程数小于核心线程数时,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满:
- 若线程数小于最大线程数,创建线程。
- 若线程数等于最大线程数,抛出异常,拒绝任务。