Java线程池源码分析(着重点请看加粗字体和代码片段的注释)
先看看线程池的类图
另外Executors中提供了几种不同的线程池可以供开发者使用。
1. ThreadPoolExecutor的构造方法及变量
1.1 ThreadPoolExecutor构造函数中的变量们 (根据官方注释翻译而来)
- corePoolSize:保留在线程池中的线程数量,即便他们空闲(除非设置allowCoreThreadTimeOut参数)
- maximumPoolSize:允许在线程池中的最大线程数量
- keepAliveTime:当线程数量大于corePoolSize时多余的空闲线程的最大存活时间
- unit :keepAliveTime参数的时间单位
- workQueue :用于存放execute方法提交的Runnable任务的阻塞队列
- threadFactory :线程工厂,用于创建线程(用户可以继承ThradFactory接口制定个性化线程)
- handler : 拒绝任务时应该使用的策略,因为线程池满了,阻塞队列也满了。
如果满足以下条件之一,则抛出IllegalArgumentException:
corePoolSize <0 keepAliveTime <0 maximumPoolSize <= 0 maximumPoolSize <corePoolSize 如果workQueue threadFactory handler为null 抛出空指针异常
1.2 ThreadPoolExecutor构造函数
构造函数用于指定上面变量的值,这里不再详细赘述。
2. ThreadPoolExecutor的内部类Worker
重点在于Worker继承了Runnable接口与AQS抽象类,重写了AQS的tryAcquire,tryRelease方法。这两方法后面会用到。
2.1 Worker中的重要成员变量
- thread:该Worker正在其中运行的线程,由threadFactor创建,如果创建失败则为空,默认是Worker自身
- firstTask:要运行的初始任务,可能为null
- completedTasks:任务计数器
2.2 Worker中的tryAcquire方法
protected boolean tryAcquire(int unused) {
//进行cas操作(加锁并将exclusiveOwnerThread变量指向当前线程)
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
2.3 Worker中的tryRelease方法
//释放锁时使用
protected boolean tryRelease(int unused) {
//设置exclusiveOwnerThread变量为null
setExclusiveOwnerThread(null);
//将状态码置为0
setState(0);
return true;
}
2.4 Worker中的run方法
public void run() {
runWorker(this);
}
乍一看这不是唬人么,runWorker方法是属于ThreadPoolExecutor的,下面我们来详细看看runWorker方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
//拿到Worker里的任务
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//如果任务本身不为null就执行当前任务;如果任务为null就从阻塞队列里拿。
while (task != null || (task = getTask()) != null) {
w.lock();
//如果池正在停止,请确保线程被中断; 如果没有,请确保线程不被中断。
//这需要在第二种情况下重新检查以应对
//清除中断时正在关闭线程池
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//这是一个空方法,用户可以继承该类并重写
beforeExecute(wt, task);
Throwable thrown = null;
try {
//调用run方法,注意是run()不是start()
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
//这是一个空方法,用户可以继承该类并重写
afterExecute(task, thrown);
}
} finally {
//将task置空方便从阻塞队列获取
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
runWorker方法中采用了while循环不断从阻塞队列里获取任务,当阻塞队列无任务时,线程会被阻塞,当然也不会一直阻塞,如果我们线程数量大于corePoolSize或者设置了allowCoreTimeOut,getTask会返回空,然后执行processWorkerExit方法去掉多余线程。
3. ThreadPoolExecutor的execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//获取正在工作的线程数量是否小于corePoolSize,小于则增加Worker执行command
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
//增加Worker失败,重新获取线程池状态
c = ctl.get();
}
//判断池子是否还在运行,如果运行就往阻塞队列里放任务
if (isRunning(c) && workQueue.offer(command)) {
//重新获取线程池状态(这里防止刚把任务放进去就关闭线程池)
int recheck = ctl.get();
//如果池子没有运行了就去从阻塞队列里尝试删除任务,删除成功后执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
//池子还在运行的状态下如果没有工作的线程了就创建线程(也是害怕前面创建的线程死了,毕竟并发不可控)
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//阻塞队列也满了的情况下就尝试在池子里再创建线程,如果失败就执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
梳理一下execute方法的整个流程:
- 获取正在运行的线程数量,如果线程数量小于corePoolSize,直接增加线程。
- 当线程数量不小于corePoolSize或增加线程失败,将任务加入阻塞队列中;添加成功还要检查一遍线程池的状态,防止刚加入阻塞队列线程池终止或没有线程在工作:如果此时线程池终止,删除刚添加到执行拒绝策略,如果没有线程工作,创建线程。
- 当阻塞队列也满的情况下,尝试创建线程去执行任务(在线程数量不大于maximum的情况下),创建失败则执行拒绝策略。
上述代码中调用了一个非常重要的方法addWorker,下面我们进入addWorker中一探究竟。
4. ThreadPoolExecutor的addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
//获取运行状态
int c = ctl.get();
int rs = runStateOf(c);
//仅在必要时检查队列是否为空
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//获取工作的线程数量
int wc = workerCountOf(c);
//线程数量大于最大值或corePoolSize程序终止返回flase(选择corePool还是max具体由参数core控制)
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//尝试cas操作将状态码c变为c+1(这个方法的实现就一行可以自己进去看)
if (compareAndIncrementWorkerCount(c))
//尝试cas成功就跳出最外层循环
break retry;
//获取最新的状态码
c = ctl.get();
//如果运行状态和之前不一样了就从最外层循环开始
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//创建一个Worker并将要执行的任务放进去
w = new Worker(firstTask);
//获取该Worker的对象
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());W
//线程池没关闭才能添加Worker
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // 检查这个线程是否已经开启了(可能是用户自定义的ThreadFactory开启)
throw new IllegalThreadStateException();
//将创建的Worker放入HashSet类型的workers中
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
//解锁
mainLock.unlock();
}
//添加Worker成功则启动线程
if (workerAdded) {
t.start();//这里就会执行runWorker()的代码
workerStarted = true;
}
}
} finally {
//线程没启动成功要回滚并返回失败
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
addWorker方法的执行流程如下:
- 检查线程池的状态有没有关闭 和 工作线程的数量是否达最大值,不够条件则方法执行失败。
- 线程池状态与工作线程数量都没有问题的情况下,使用cas原子操作尝试使线程数量+1;若执行失败则再次检查线程池状态决定是否继续
- cas操作成功,创建Worker,在全局锁的模式下 将Worker对象加入set序列(HashSet不是线程安全的),释放锁,并启动线程执行任务;若线程执行失败,回滚之前的操作。
5. 线程池拒绝策略
在线程池执行过程中,满足特定情况下线程池会“拒绝”任务,这个拒绝策略可以由我们开发人员自定义,当然线程池也提供了几个默认实现的拒绝策略,接下来我们一探究竟。
上面给出我们线程池默认实现的几个拒绝策略,可以看到都是实现RejectedExecutionHandler接口。
点进去看到仅有一个rejectedExecution方法,接下来我们看看ThreadPoolExecutor提供的四个默认实现。
5.1 AbortPolicy
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* 抛出rejectedExecution打印拒绝的任务信息
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
可以看到在AbortPolicy中仅仅是抛出被拒绝的任务异常信息。
5.2 CallerRunsPolicy
public CallerRunsPolicy() { }
/**
* 判断线程池是否关闭,没有关闭则调用任务的run方法
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
我们可以看到,在CallerRunsPolicy中的拒绝策略是:如果main线程向线程池添加任务被拒绝,则由main线程执行任务的run方法
这样的话,main方法可能会在改任务执行结束前不再向下运行,所以根据自己的实际需要使用这种拒绝策略。
5.3 DiscardPolicy
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* 什么也不做
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
对,你没有看错,就是什么也没做,这是个空方法。
5.4 DiscardOldestPolicy
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* 如果线程池没关闭将阻塞队列的头节点任务删除,然后再将要执行的任务重新丢进线程池的execute
* 方法
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
这个拒绝策略会将阻塞队列的第一个任务删除掉然后再尝试将要执行的任务丢进线程池继续执行。
=======================================
shutdown方法后续再更