目录
简介
自定义线程池的补充
线程池原理
submit方法和execute方法的区别
addWorker方法
run方法
runWorker方法后processWorkerExit方法
异常处理
各位早上好下午好晚上好,吃了么您
ok,现在让我们来研究一下java线程池底层原理及对应的部分源码
简介
首先,什么是线程池,线程池有哪些,介个玩意咱就不过多仔细介绍了
本文主要用来补充线程池底层原理,并分析部分源码的
首先,我们已经知道我们常用的线程池就这四种
FixedThreadPool
CachedThreadPool
SingleThreadExecutor
ScheduledThreadPool
其中,前三种的底层创建方式,也就是构造方法,都是使用的
ThreadPoolExecutor
都是使用这个实现的,只不过对应的参数不同而已
自定义线程池的补充
关于这个自定义线程池如何使用
1、核心线程数并非可以一直存活,当 allowCoreThreadTimeout 设置为 true 的时候,核心线程也会被超时回收
2、线程池最大大小,指的是核心线程数+临时线程数
3、最大空闲时间和单位,都是针对临时线程的,而不是针对核心线程的
4、当线程数不够用的时候,任务会被放进阻塞队列
5、当任务队列满了,线程池也达到了最大数量,就会执行拒绝策略,默认策略为丢弃任务并抛出异常
补充一下其他线程池的信息
- newCachedThreadPool中,传入的核心线程数为0,线程池最大为MAX,队列为synchronousQueue,说明线程都是临时线程,且由于队列的原因,这是一个生产消费队列,可以理解为队列容量为1,如果队列有任务,下个任务就无法存入,新任务先进队列,然后才会从队列被取走执行
- newFixThreadPool,要求传入核心线程数大小和线程池的最大数量,意味着无临时线程,使用阻塞队列LinkedBlockingQueue,新任务直接执行,执行不了放队列
- newSingleThreadExecutor,核心线程数为1,线程池最大为1,使用阻塞队列LinkedBlockingQueue
注意,这儿有个地方要注意,可缓冲线程池,也就是 newCacheThreadPool,对线程池最大数量未做限制,所以容易出现线程过多,CPU拉满的情况,谨慎使用
线程池原理
原理,其实就是一张图
submit方法和execute方法的区别
我们在线程池提交任务的时候,有两种方法可以提交上去,一个是submit,一个是execute,那这两个有什么区别呢?
我们直接去看
可以看到,submit有返回值,返回了Future,而execute没有返回值
并且,我们点进去submit方法
可以看到,submit方法内部调用了execute方法,将任务传了下去
我们来看看 execute 方法执行逻辑,注意看好类,是 ThreadPoolExecutor 类
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
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方法的执行逻辑,可以看到,是有一些判断的,我们就一步一步来
上来第一个判断,可以很清晰的看到,它判断我们要执行的任务是不是为null,如果是null,直接抛空指针
下面的看起来就有点麻烦了
从名字上我们就可以看到,workerCountOf(c) 在统计工作线程数量,corePoolSize是核心线程数量
所以,这一段逻辑,我们可以化为这样
if(工作线程数量 < 核心线程数量) {
创建核心线程
}
if(线程在运行状态 && 尝试把任务提交到队列) {
if(!线程不是运行状态 && 尝试从队列移除任务) {
拒绝策略
} else if (工作线程数==0) {
把任务交给非核心线程
}
} else if (!能否把任务分给非核心线程) {
拒绝策略
}
这样是不是很明确就能看明白这一段逻辑了?
当然,我们可以看到,有一个很重要的方法,addWorker方法,这个其实就是创建工作线程,我们下面解释
注意一个地方
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
如果工作线程数为0,那就创建一个 null 任务,交给非核心线程运行,这有什么意义呢?
如果创建一个null任务,任务在队列存在了,就会被取出,保证线程池在 running 状态的时候,有一个任务在执行
addWorker方法
在上面,有好几个地方都执行了这个方法,说明这个方法很重要
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 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;
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 {
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();
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;
}
我们慢慢来看这个方法,一眼看过去,代码分为两部分,一部分为自旋,或者直接叫它死循环,一部分为其他处理逻辑,我们一部分一部分来看
首先,我们看自旋代码,自旋代码分为两部分,一部分是判断,另一部分是另一个自旋,我们先看判断
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
我们可以看到,这是在判断线程的状态
采用了高低位来判断,就是判断是不是 SHUTDOWN 或以后的状态,并且任务为空,队列不为空,这个情况下,就不再增加 addWorker
然后我们继续看这个小自旋
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
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
}
我们看到,第一行调用了一个方法,从方法的名字我们可以知道,它是在统计数量,统计什么数量呢?单单从这儿看,也不好说
我们看下面,有一个判断,wc是不是大于括号内计算出来的数量,括号内很明显,就是一个三目运算,这个core,是addWorker方法传进来的,也就是说,如果是核心线程,要判断wc是否大于等于核心线程数,如果是非核心线程,要判断是否大于等于线程池最大线程数
这儿其实就是判断是否会超出限制,如果超出了,那就直接返回一个false,否则就继续
然后我们看到,下一个判断调用了一个方法 compareAndIncrementWorkerCount(c)
我们看看这个方法是什么
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
我们可以看到,它其实就是用CAS去对数量加了一个1,增加成功后,退出这个自旋
OK,到这里,addWorker的第一部分,也就是大的这部分自旋已经结束了,剩下的就是后半部分的逻辑,让我们来看看它做了什么(为了防止晕,我把后半部分代码再单独贴出来)
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
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();
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;
可以看到,try里面,第一行,用传入的任务创建了一个worker,这个Worker就是真正要干活的
浅浅看一下这个类是什么样的
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
它继承了AQS,实现了Runnable,AQS先不管,实现Runnalbe,那就说明得实现run方法,而run方法的运行时机,就是.start()的时候,这个我们先记住
再看一下这个类的属性和构造方法
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
三个属性,分别是线程、任务、完成任务的数量
我们暂时称呼new 出来的这个worker为工作者
首先,获取了工作者的线程,看为不为空,不为空的话,执行一大段逻辑,我们先看简单的,如果为空怎么办
如果为空的话,那就没有任何可以执行的,也就是说,finally里面的代码将会被执行,然后返回false,addWorker失败,因为默认 workerStarted 为false
从名字我们知道,finally的这个 if,就是用来执行添加worker失败的情况的,走,看看这个方法
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
一目了然,加锁,从workers移除创建的worker,然后尝试减少worker的数量,记不记得,刚开始大自旋的时候,用CAS加了1,然后尝试结束,最后释放锁
我们回来,看这个 if 里执行了什么逻辑,当创建的worker的线程不为空的时候
可能有多个并发执行这个任务,所以先上锁,开始执行
但,在执行过程中也是需要判断状态的,有可能程序会出现中断或者其他情况
这儿还有一个判断,如果线程还没有启动,但线程处于存活状态,那说明现在是有问题的,这种情况,会直接抛异常
当把这种情况排除掉了,那它就把有效的这种worker,添加到workers中,然后判断一下workers的数量没有有大于设置的最大线程数
将workerAdded设置为true,释放锁
如果已经添加了,那么就调用worker的.start() 方法,并将workerStarted 标记为true,记不记得我们上面说的,什么时候Runnable的run方法会启动,那就是调用.start() 方法的时候
这就是这块的源码,当然,从代码上我们也可以看出优先级问题
假设提交一个任务
优先级1:核心线程池满了没,没满,创建核心线程执行,满了,执行优先级2
优先级2:队列满了没,没满,任务放在队列里,满了,执行优先级3
优先级3:线程池满了没,没满,创建非核心线程执行任务,满了,执行拒绝策略
run方法
我们已经知道,.start() 方法,会执行 run 方法,那么,run方法怎么执行的呢?
我们直接看线程池的run方法
public void run() {
runWorker(this);
}
哦?runWorker?
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
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);
Throwable thrown = null;
try {
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 = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
可以看到,第四行,对 runWorker进行了解锁,这个地方,可能乍一看有点懵,啥意思?
仔细看,后边的注释,允许中断,再过去,看一下 Worker 的构造方法
看这句话,这句注释,在 runWorker 之前,不允许中断
state是什么?
其实 state 就是AQS内部维护的属性,表示资源可用状态,这个等之后AQS的时候就知道了
我们看 while 里面的条件,当task不为null的时候,才会从队列中去取任务,这一点也印证了队列的优先级确实是低,执行优先级上,左边大于右边
这一点,也可以理解为节省资源,毕竟,一个工作者手上有任务不做,再去从队列拿,不大好不大好~
然后在加锁之后,有一段判断,是什么意思呢?
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
这一段意思,其实就是在判断线程池的状态,是不是 STOP,如果是 STOP,那就中断,真正执行逻辑的,就是下面的try cache 代码块
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
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);
}
比较显眼的 就是 beforeExecute 和 afterExecute,如果点进去看,会发现没有实现,这意味着我们可以根据实际需要去做这个实现
去除这两个代码,其实核心代码就是 task.run()
看好,在这个 finally 执行 afterExecute 方法之后,后边还有一个 finally
finally {
task = null;
w.completedTasks++;
w.unlock();
}
这个就很简单了,任务置为null,worker的完成数量加一,解锁
加油,就快完了!
回顾一下,一开始的 try,还有一个最后最大最外层的 finally
runWorker方法后processWorkerExit方法
这是最后执行的退出方法,也是非常重要的
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
前面的我们也不用关注很多,我们看最后,满足条件的话,它会调用 addWorker,创建了一个null任务,非核心线程
综合来说,其流程是这样的
异常处理
通过submit提交的线程,可以屏蔽线程中产生的异常,可以保证线程的复用,当使用get的时候,才会抛出对应的异常
所谓屏蔽异常,其实是将异常保存了,future.get的时候,就会返回对应的异常
可以看一下 FutureTask的run方法
看这个 outcome 的注释
我们再来看看get方法
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
可以看到,最后有一个 report 方法
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
可以看到,outcome是异常时才抛出
至于,为什么让大家看 FutureTask 类呢?因为就是基于它在运行,不信?咱瞧瞧
咱们看看这个方法是什么,返回了一个 RunnalbeFuture
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
看,创建了一个 FutureTask,至于它本什么