this.corePoolSize = corePoolSize; // 核心池大小
this.maximumPoolSize = maximumPoolSize; // 线程池最大容量
this.workQueue = workQueue; // 阻塞队列
this.keepAliveTime = unit.toNanos(keepAliveTime); // 非核心线程存活时间
this.threadFactory = threadFactory; // 线程工厂
this.handler = handler; // 拒绝策略
} else {
throw new NullPointerException();
}
} else {
throw new IllegalArgumentException();
}
}
都是熟悉的参数,线程池的构造器实现还是很简单的,可以看到就是初始化了一些核心参数,没有什么逻辑操作,初始化流程结束了,只要熟悉线程池的核心参数,这个过程看起来就很简单了。
### 2.2 运行任务
运行任务是分析线程池原理最重要的部分,使用线程池就是要让它执行任务,所以线程池的所有核心逻辑代码的起点就是用户提交一个任务,用户提交任务最简单的代码如下:
ExecutorService service = Executors.newCachedThreadPool();
service.execute(() -> {
// 要执行的逻辑代码
});
提交任务是通过ThreadPool的execute()方法发起的,先来看看这个方法的逻辑:
public void execute(Runnable command) {
// 参数检查
if (command == null) {
throw new NullPointerException();
} else {
int c = this.ctl.get();
if (workerCountOf© < this.corePoolSize) {
// 当前正在运行的线程数小于核心大小,直接创建核心线程运行该任务
if (this.addWorker(command, true)) {
return;
}
c = this.ctl.get();
}
// 尝试添加到阻塞队列中
if (isRunning(c) && this.workQueue.offer(command)) {
int recheck = this.ctl.get();
if (!isRunning(recheck) && this.remove(command)) {
this.reject(command);
} else if (workerCountOf(recheck) == 0) {
this.addWorker((Runnable)null, false);
}
} else if (!this.addWorker(command, false)) { // 尝试创建非核心线程运行
// 无法创建非核心线程,线程池已满,触发拒绝策略
this.reject(command);
}
}
}
这个方法的内部逻辑很符合之前对核心参数的解释,根据这个方法的执行逻辑可以推断出线程池创建线程的基本流程:
> 1. 判断正在运行的任务数是否小于核心线程数,如果小于核心线程数那么直接创建一个核心线程运行这个任务。
>
> 2. 如果目前正在运行的任务数大于核心线程数,那么尝试添加到阻塞队列中等待调度运行。
>
> 3. 如果阻塞队列已满无法添加到阻塞队列中,那么尝试创建非核心线程运行,如果创建非核心线程失败(线程池容量已满),那么会触发拒绝策略。
>
以上代码中可以发现,不论是创建核心线程还是非核心线程都是通过`Thread.addWorker(Runnable,boolean)`方法实现的:
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;😉 {
// 记录了线程池中运行的线程数量和线程的状态,高3位为状态,低29位为线程数
int c = ctl.get();
// 线程运行状态
int rs = runStateOf©;
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 线程数
int wc = workerCountOf(c);
// 检查是否超出线程池的容量
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 将记录线程数的变量+1
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 如果没有成功创建,那么将会重试
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
// 初始化线程状态
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
/*
* 创建一个新的线程,此处worker实际是线程池
* 为了方便调度/获取线程状态而创建的封装类,
* 其实worker可以认为是一个线程。这里的线程
* 是Worker内部实例化时自动创建的。
*/
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 进入临界区之前加上可重入锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
// 状态检查
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 添加存储Worker的集合中方便后续管理
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 成功加入后启动线程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
代码很多,但是逻辑很简单,就是进行了线程池内线程数量的检查和线程运行状态检查,对于核心线程来说只要当前运行的线程数 < 核心线程数,那么可以直接创建核心线程并运行;对于非核心线程来说如果当前运行的线程数 < 线程池容量,那么可以直接创建非核心线程并运行。
需要注意的一点是,线程池内的某个线程是没有核心线程与非核心线程的区别的,所谓的核心线程资源不被回收的意思是线程池内永远会保证有N(核心线程数)个线程没有被释放掉。
### 2.3 实现线程复用
通过上面的代码了解到,线程池直接调度的资源并不是线程,而是`Worker`类:
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
// 线程池中实际运行的线程
final Thread thread;
// 用户提交的任务
Runnable firstTask;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
// 利用线程工厂创建线程
this.thread = getThreadFactory().newThread(this);
}
// 运行用户提交的任务
public void run() {
runWorker(this);
}
{
这里的设计很巧妙,Worker持有的线程对象的Runnable实例其实就是自己,所以当运行Worker中的Thread时调用的是Worker的run方法,下面来看一下runWorker方法的具体实现:
final void runWorker(Worker w) {
// 这个线程就是上面初始化完成Worker后启动的线程
Thread wt = Thread.currentThread();
// Worker中要执行的任务
Runnable task = w.firstTask;
w.firstTask = null;
结尾
如何才能让我们在面试中对答如流呢?
答案当然是平时在工作或者学习中多提升自身实力的啦,那如何才能正确的学习,有方向的学习呢?有没有免费资料可以借鉴。为此我整理了一份Android学习资料路线:
这里是一份BAT大厂面试资料专题包:
好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。
7940226)]
这里是一份BAT大厂面试资料专题包:
[外链图片转存中…(img-mAurRKpy-1630577940228)]
好了,今天的分享就到这里,如果你对在面试中遇到的问题,或者刚毕业及工作几年迷茫不知道该如何准备面试并突破现状提升自己,对于自己的未来还不够了解不知道给如何规划。来看看同行们都是如何突破现状,怎么学习的,来吸收他们的面试以及工作经验完善自己的之后的面试计划及职业规划。