今天来介绍一下面试几乎是必问的内容:线程池,那么在Java,JUC并发工具包中提供了Executors的工具类来操作线程池,而它实际就是使用的ThreadPoolExecutor,那下面就来从源码角度分析一下ThreadPoolExecutor。
属性
-
ctl
ctl是AtomicInteger类型的数值,它用来表示线程池的控制状态,ctl的值存储了两个不同的状态值:工作线程数和线程状态。ctl的前三位用来表示线程状态,后29位用来表示工作线程数

-
线程运行状态
RUNNING = -1 << COUNT_BITS(11100000 ... ... 00000000)
SHUTDOWN = 0 << COUNT_BITS(00000000 ... ... 00000000)
STOP = 1 << COUNT_BITS(00100000 ... ... 00000000)
TIDYING = 2 << COUNT_BITS(01000000 ... ... 00000000)
TERMINATED = 3 << COUNT_BITS(01100000 ... ... 00000000)
-
其他属性值
COUNT_BITS = Integer.SIZE - 3(29)
CAPACITY = (1 << COUNT_BITS) - 1(2^29-1),用二进制表示:0001111 11111111 11111111 11111111
工作线程数目、线程状态以及ctl值通过下面的这三个方法来计算出相应的值
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
构造函数
ThreadPoolExecutor的参数有哪些也是面试中经常问的题目,下面我们就拿参数最多的构造函数来介绍一下。
corePoolSize:线程池核心池数目,即使线程是空闲的,也不会被回收(allowCoreThreadTimeOut默认为false,如果设置为true,则核心池空闲时,会被回收)
maximumPoolSize:线程池中允许的最大线程数目
keepAliveTime:大于核心池的线程空闲时,能在线程池中存在的最大时间
unit:keepAliveTime的时间单位
workQueue:当大于核心池数目的任务提交时,会加入到队列中排队
threadFactory:创建线程的工厂
handler:当线程池满了后,线程池的拒绝策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
拒绝策略
jdk自带实现了4种拒绝策略。
-
AbortPolicy
直接抛出异常RejectedExecutionException
-
CallerRunsPolicy
由调用者来执行本次任务
-
DiscardPolicy
直接丢弃任务,什么都不处理
-
DiscardOldestPolicy
丢弃最旧的任务,再去execute本次任务
execute方法
首先来看execute方法执行的三个步骤,然后再去看一下它的源码,详细分析线程池的实现原理。
- 如果线程池中运行的线程数目小于corePoolSize,尝试启动新的线程来执行任务
- 如果任务能成功加入到队列中,仍然需要二次检查看是否需要创建一个线程(因为存在的一个线程可能在上次检查之后已经停止了)或者线程池已经shutdown了。因此再次检查这个状态,并且如果线程池停止了就需要把刚才入队的任务出队,或者启动一个新的线程
- 如果任务加入队列失败,那么就去尝试添加一个新线程,如果失败,就去执行拒绝策略。
public void execute(Runnable command) {
//任务command不能为空
if (command == null)
throw new NullPointerException();
//获取ctl值
int c = ctl.get();
//第一步:小于核心池数目,创建线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
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方法源码也可以看出执行的三个步骤,下面来看一下addWorker方法,这个方法主要是创建线程,执行任务。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get(), rs = runStateOf(c);
// 这个判断的意思是:线程池状态大于SHUTDOWN的话,不再执行任务;
// 等于SHUTDOWN的话,不接收新任务,队列中还有任务的话继续执行
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;
}
}
boolean workerStarted = false, workerAdded = false;
Worker w = null;
try {
// 创建新worker,Worker类实现了Runnable接口,所以worker里面有需要执行的任务
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
//再次检查线程池运行状态,并且要求线程池是RUNNING状态,或者SHUTDOWN状态且新任务为null才可以
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // t是刚创建出来的worker的thread,还未启动就是alive的话抛出异常
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//启动新创建出来的线程,t.start之后回去调用worker里面的run方法,
//下面会详细解析worker的run方法
t.start();
workerStarted = true;
}
}
} finally {
//失败的话,做回滚操作
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
addWorker中会加入新线程,并启动线程,大家都知道线程启动后回去调用Runnable的run方法,这里也一样,会去调用Worker的run方法(Worker的run方法调用了runWorker方法,所以我们可以直接看runWorker方法的逻辑。省略了一些代码,只看主要的逻辑)
final void runWorker(Worker w) {
...
w.unlock();
try {
//这里判断如果w.firstTask不为null,则去直接执行这个task
//如果w.firstTask为null,则通过getTask方法去队列中取任务
while (task != null || (task = getTask()) != null) {
w.lock();
...
try {
beforeExecute(wt, task);
try {
//执行任务的run方法
//执行任务的前后可以自定义一些执行逻辑,beforeExecute和afterExecute
task.run();
} catch (...) {
...
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
//在最后的总结中会有这个方法的流程图
processWorkerExit(w, completedAbruptly);
}
}
再来看一下从队列中取任务的getTask的逻辑(省略了一些代码,只看主要的逻辑)
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
...
int wc = workerCountOf(c);
//如果允许核心线程超时回收或者worker数目大于核心线程数目,为true
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
...
try {
//如果线程池中的线程需要超时回收的,则以poll的方式从队列取任务,超时返回null
//否则以take阻塞的方式从队列取任务
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
总结
ThreadPoolExecutor的主要逻辑基本上都介绍了,最后整理了ThreadPoolExecutor的execute方法和processWorkerExit的流程图,大家可以参考。