上一篇博客大概了解了下线程池是什么,这篇博客将在源码的基础上去验证上一篇博客中提到的
Thread执行流程。我的博客保证是一个字一个字敲出来的
1.线程池源码解析
在ThreadPoolExecutor类中,最核心的任务提交方法是execute()方法,所以就先从execte方法开始
来看看线程池的执行流程:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 活动线程数 < corePoolSize
if (workerCountOf(c) < corePoolSize) {
// 直接启动新的线程。第二个参数true:addWorker中会重新检查workerCount是否小于corePoolSize
if (addWorker(command, true))
// 添加成功返回
return;
c = ctl.get();
}
// 活动线程数 >= corePoolSize
// runState为RUNNING && 队列未满
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// double check
// 非RUNNING状态 则从workQueue中移除任务并拒绝
if (!isRunning(recheck) && remove(command))
reject(command);// 采用线程池指定的策略拒绝任务
// 线程池处于RUNNING状态 || 线程池处于非RUNNING状态但是任务移除失败
else if (workerCountOf(recheck) == 0)
// 这行代码是为了SHUTDOWN状态下没有活动线程了,但是队列里还有任务没执行这种特殊情况。
// 添加一个null任务是因为SHUTDOWN状态下,线程池不再接受新任务
addWorker(null, false);
// 两种情况:
// 1.非RUNNING状态拒绝新的任务
// 2.队列满了启动新的线程失败(workCount > maximumPoolSize)
} else if (!addWorker(command, false))
reject(command);
}
注释比较清楚了就不再解释了,其中比较难理解的应该是
addWorker(null, false);
这一行,这要结合addWorker一起来看。 主
要目的是防止HUTDOWN状态下没有活动线程了,但是队列里还有任务没执行这种特殊情况。
我们来看看addWorker(command,true)这个方法的内部实现:
private boolean addWorker(Runnable firstTask, boolean core) {
retry: for (;;) {
int c = ctl.get();
int rs = runStateOf(c);// 当前线程池状态
// Check if queue empty only if necessary.
// 这条语句等价:rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null ||
// workQueue.isEmpty())
// 满足下列调价则直接返回false,线程创建失败:
// rs > SHUTDOWN:STOP || TIDYING || TERMINATED 此时不再接受新的任务,且所有任务执行结束
// rs = SHUTDOWN:firtTask != null 此时不再接受任务,但是仍然会执行队列中的任务
// rs = SHUTDOWN:firtTask == null见execute方法的addWorker(null,
// false),任务为null && 队列为空
// 最后一种情况也就是说SHUTDONW状态下,如果队列不为空还得接着往下执行,为什么?add一个null任务目的到底是什么?
// 看execute方法只有workCount==0的时候firstTask才会为null结合这里的条件就是线程池SHUTDOWN了不再接受新任务
// 但是此时队列不为空,那么还得创建线程把任务给执行完才行。
if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
return false;
// 走到这的情形:
// 1.线程池状态为RUNNING
// 2.SHUTDOWN状态,但队列中还有任务需要执行
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))// 原子操作递增workCount
break retry;// 操作成功跳出的重试的循环
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)// 如果线程池的状态发生变化则重试
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
// wokerCount递增成功
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
// 并发的访问线程池workers对象必须加锁
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int c = ctl.get();
int rs = runStateOf(c);
// RUNNING状态 || SHUTDONW状态下清理队列中剩余的任务
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 将新启动的线程添加到线程集合中,主要为了计算线程池线程的数量
workers.add(w);
// 更新largestPoolSize
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 启动新添加的线程,这个线程首先执行firstTask,然后不停的从队列中取任务执行
// 当等待keepAlieTime还没有任务执行则该线程结束。见runWoker和getTask方法的代码。
if (workerAdded) {
t.start();// 最终执行的是ThreadPoolExecutor的runWoker方法
workerStarted = true;
}
}
} finally {
// 线程启动失败,则从wokers中移除w并递减wokerCount
if (!workerStarted)
// 递减wokerCount会触发tryTerminate方法
addWorkerFailed(w);
}
return workerStarted;
}
代码有点复杂,但静下来也还看得下去,源码都这样枯燥但有味道。这个方法的后面根据提交的Runnable任务实例化了一个
Worker对象,在Worker构造方法中根据ThreadFactory线程工程创建一个Thread对象
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
Worker类实现了Runnable接口,我们来看看Worker的代码,主要看run方法中的runWorker方法,这个方法非常重要
简单来说它做的就是:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// Worker的构造函数中抑制了线程中断setState(-1),所以这里需要unlock从而允许中断
w.unlock();
// 用于标识是否异常终止,finally中processWorkerExit的方法会有不同逻辑
// 为true的情况:1.执行任务抛出异常;2.被中断。
boolean completedAbruptly = true;
try {
// 这里第一次进来task不为空会执行firstTask,然后设置firstTask为null,这样
//task = getTask()的逻辑才会被执行,这样会从队列中去任务执行
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))
&& !wt.isInterrupted())
wt.interrupt();
try {
// 任务执行前可以插入一些处理,子类重载该方法
beforeExecute(wt, task); T
Throwable thrown = null;
try {
task.run();// 执行用户任务,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 {
// 和beforeExecute一样,留给子类去重载
afterExecute(task, thrown); }
}
finally {
//设置task为空,不然不会去执行getTask的逻辑
task = null;
w.completedTasks++;
w.unlock(); }
}
completedAbruptly = false;
}
finally {
// 结束线程的一些清理工作
processWorkerExit(w, completedAbruptly);
}
}
getTask()是ThreadPoolExecutor类中的方法,并不是Worker类中的方法,下面是getTask方法的实现:
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 1.rs > SHUTDOWN 所以rs至少等于STOP,这时不再处理队列中的任务
// 2.rs = SHUTDOWN 所以rs>=STOP肯定不成立,这时还需要处理队列中的任务除非队列为空
// 这两种情况都会返回null让runWoker退出while循环也就是当前线程结束了,所以必须要decrement
// wokerCount
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 递减workerCount值
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 判断如果设置允许为核心池中的线程设置空闲存活时间或者前者设置为false,当前线程数量大于corePoolSize
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,取不到时返回null,
//take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到Blocking有新的对象被加入为止
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
判断如果当前线程池的是否设置allowCoreThreadTimeOut为true,如果为true,则调用
poll(keepAliveTime,TimeUnit.NANOSECONDS)去阻塞队列取线程,如果取不到(队列为空),那么就会等到keepAliveTime单位时间后
返回null,并且关闭该线程,即时是核心线程,否则会使用take()方法去线程池取线程,take也是从队列里面取出任务如
果队列是空的则阻塞保证线程池里面的核心线程数量的线程一直存在
好了整个流程下来可能有点蒙逼,我们回过头整理一下整个流程,当我们调用ThreadPoolExecutor类的execute方法提交一个线
程任务时,会开启一个线程去执行该任务,任务的执行方法中,会执行该任务,任务执行完后去阻塞队列去第一个线程执行,线程
池如果设置allowCoreThreadTimeOut或者线程数量大于核心线程数量,那么会调用poll(keepAliveTime,TimeUnit.NANOSECONDS)去
队列取线程任务,如果队列为空则等待keepAliveTime单位时间后返回null并且关闭该线程,否则会调用take()方法去取线程,这个方法会阻塞该线程,知道阻塞队列不为空,利用该方法可以保证核心线程数量的线程一直存在;当线程数量大于核心线程数量时会把线程添加到阻塞队列,当阻塞队列满了,并且正在运行的线程数量+阻塞队列中的线程数量<最大线程数量,会继续调用addWorker执行上面的操作,否则会执行拒绝操作,抛出异常
2.一个模拟ThreadPoolExecutor的例子
public final class ThreadPool {
// 线程池中默认线程的个数为5
private static int worker_num = 5;
// 工作线程
private WorkThread[] workThreads;
// 任务队列,作为一个缓冲,List线程不安全
private List taskQueue = new LinkedList();
private static ThreadPool threadPool;
// 创建具有默认线程个数的线程池
private ThreadPool() {
this(5);
}
// 创建线程池,worker_num为线程池中工作线程的个数
private ThreadPool(int worker_num) {
ThreadPool.worker_num = worker_num;
workThreads = new WorkThread[worker_num];
for (int i = 0; i < worker_num; i++) {
workThreads[i] = new WorkThread();
workThreads[i].start();// 开启线程池中的线程
}
}
// 单态模式,获得一个默认线程个数的线程池
public static ThreadPool getThreadPool() {
return getThreadPool(ThreadPool.worker_num);
}
// 单态模式,获得一个指定线程个数的线程池,worker_num(>0)为线程池中工作线程的个数
// worker_num<=0创建默认的工作线程个数
public static ThreadPool getThreadPool(int worker_num1) {
if (threadPool == null)
threadPool = new ThreadPool(worker_num1);
return threadPool;
}
// 执行任务,其实只是把任务加入任务队列,什么时候执行有线程池管理器觉定
public void addTask(Runnable task) {
synchronized (taskQueue) {
taskQueue.add(task);
taskQueue.notifyAll();
}
}
// 销毁线程池,该方法保证在所有任务都完成的情况下才销毁所有线程,否则等待任务完成才销毁
public void destroy() {
while (!taskQueue.isEmpty()) {// 如果还有任务没执行完成,就先睡会吧
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 工作线程停止工作,且置为null
for (int i = 0; i < worker_num; i++) {
workThreads[i].stopWorker();
workThreads[i] = null;
}
threadPool=null;
taskQueue.clear();// 清空任务队列
}
/**
* 内部类,工作线程
*/
private class WorkThread extends Thread {
// 该工作线程是否有效,用于结束该工作线程
private boolean isRunning = true;
/*
* 关键所在啊,如果任务队列不空,则取出任务执行,若任务队列空,则等待
*/
@Override
public void run() {
Runnable r = null;
while (isRunning) {// 注意,若线程无效则自然结束run方法,该线程就没用了
synchronized (taskQueue) {
while (isRunning && taskQueue.isEmpty()) {// 队列为空
try {
taskQueue.wait(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (!taskQueue.isEmpty())
r = taskQueue.remove(0);// 取出任务
}
if (r != null) {
r.run();// 执行任务
}
r = null;
}
}
// 停止工作,让该线程自然执行完run方法,自然结束
public void stopWorker() {
isRunning = false;
}
}
}
这个模拟的例子跟真正的线程池执行步骤还是有点出路,但是原理很像,可以帮助我们更好的理解
下一章将学习一下线程池具体的使用