java线程池JVM源码解析
概述
对于java的线程池,刚开始接触的时候可能就知道使用Executors.newFix……之类的方法,java线程池有三种;
固定大小的
无限伸展的
单个的single
还有就是大于核心池子怎么样。其实际上这个三个池子都是基于java提供的最基本的 ThreadPoolExecutor 进行开发的,只是传递了不同的参数而已。
线程池出现的背景(线程复用)
线程池也是一种池化技术,主要是为了实现资源的复用,即需要创建很多的线程去执行任务的时候,不必要为每个任务创建一个线程,这样有两个缺点:
1线程的数量不好控制,容易增加jvm的内存消耗
2没创建一个线程后,在当线程运行完毕后,都需要操作系统对其进行创建--->执行---->销毁增加了操作系统的负担。
使用线程池能够克服上述缺点:
1 线程池能够将线程的数量控制在一定的范围内,可以达到控制的目的,不会轻易消耗完jvm的内存
2 线程池中的线程可以复用,即一个线程一旦创建,该线程可以执行很多任务,而不需要为每个任务(Runnable对象)创建线程
核心线程池 ThreadPoolExecutor
java中所有的线程池都是基于ThreadPoolExecutor开发出来的,因此好多developer都是基于该基类来实现自己的线程池(开源的组件hive、oozie等)。线程池执行任务的时候提供的接口是execute(Runnable runable),将对象一直往池子里面装,池子一直在消耗里面的任务。下面分析execute的方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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);
}
看这段源码之前,先需要明白一个东西:线程池状态的判断方法、线程池中线程个数的判断方法
int c = ctl.get();//AtomicInteger的get方法,取出其中的数值
workerCountOf(c)//判断当前线程中有几个正在running的线程
isRunning(c)//线程池的状态
前期预备知识
这三行代码的作用再注释中已经标明,但是这里主要看下这个是怎么实现的
// Packing and unpacking 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; }
实际上就是进行一系列的位运算,具体也不清楚怎么算的。
线程池执行流程
上面的代码来源于java的 系统类加载器加载的 rt.jar中的ThreadPoolExecutor源码。往线程池中添加任务的时候,主要分为三个if判断:
第一个if
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
这段代码的意思是:
当前线程池中的线程个数<corePoolSize(核心池)大小的时候,直接执行addWorker方法,为任务对象(Runnable对象command)创建新的线程,并且更新c的数量加一(池子中线程数量增加一个,具体逻辑再addWorker中)
这里addWorker方法中传递的为true,即往核心池子里面加(后面的addWorker方法会描述),即小于coresize大小就行
第二个if
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);
}
如果当前线程池的状态为RUNNING状态,并且任务对象command也成功的插入到队列中了。
接下来再次判断线程池的状态:
如果此次发现线程池的状态出现异常,并且刚才最外层if插入到队列中的任务成功的出队,这个时候执行拒绝策略
通俗的意思就是当前线程池出现异常,就把刚才入队的任务执行拒绝策略,并抛异常出去
else if
如果线程池状态正常,或者当前队列中没有成功吧这个任务移除,(逻辑与翻转后应该变成或运算),那就说明这个任务已经被其他的线程从队列中拿走了 【这是else逻辑】
并且当前运行的线程数量为0(workerCountOf(recheck) == 0),那么这个时候意味着:
当前的任务已经被执行了,而且池子中的线程也已经全部被销毁了,但是队列中是个啥情况还不知道,因此
这个时候执行addWorker,注意此时的addWorker方法中的command是个null,即addWorker(null, false); 这个方法的含义是消耗队列中的任务,一直到队列为空(线程复用也就在这里体现)。
这里的addWorker方法传递的是falses,即往最大池子里面添加,即小于maxsize就行
其实就是一个收尾的过程
最后一个if
else if (!addWorker(command, false))
reject(command);
当线程池不在RUNNING或者是插入队列失败的情况下:再次执行一个addWorker(command),如果addWorker也失败,执行拒绝策略。如果执行成功,则创建一个新的线程执行当前任务和消耗队列的任务。
线程池不在RUNNING,addWorker方法内部会首先判断这个状态,如果不在RUNNIN,直接返回false,因此最后一个if中如果线程池不在RUNNING,则addWorker必然失败。
如果是因为queue.offer方法失败了,那则是因为队里满了,所以这个时候会再创建一个worker来执行当前command,并且帮着其他的worker一起消耗队里里面的runnable。
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 {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
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);
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;
}
上面是jvm中addWorker方法方法,这里分两个部分来分析
1 线程池判断部分
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
}
}
第一小部分
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
该部分的意思是:判断线程池的状态
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
如果:
线程池状态异常 (STOP、TIDYING、TERMINATED),并且下面的两个满足其中一个
任务 firstTask 不为空(不消耗队列)或者
队列为空
则addWorker方法错误,执行拒绝策略。
通俗的意思就是 当前线程池状态异常了
如果你还往里面添加command(对应任务 firstTask 不为空),则执行拒绝策略。线程池都异常了,还提交啥任务
如果当前command为空但是队列已经空了(对应上述队列为空),则也执行拒绝策略。线程池都异常了,addWorker传进来null本来是消耗队列里面的任务,结果队列都空了,那还执行啥,拒绝策略。
第二小部分
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
}
这个线程池中的coresize和maxsize只在这一处体现。
如果addWorker里面传递是true,则比较当前的线程池中的线程数量和coresize比较,如果大于core则执行addWorker失败
通俗的意思就是,前面执行addWorker的时候如果选择往核心池子里面加,就要判断核心池子是否越界了。
这里肯能会有疑问说,添加的时候选择true必然知道当前的数量是小于coresize的,为什么addWorker的时候还要判断一次呢?
这是因为,真正要添加的时候,线程池里面的数量可能已经很多了,毕竟添加线程这个活动不会根据第一次的结果,可能线程池一直在往里面塞任务。
如果addWorker传递的是false,则检查maxsize的数量。如果发现数量和刚开始的不一样了,则继续循环,说明当前有线程退出了。需要重新再执行一遍。
第三小部分
这里才进入到了真正的创建线程的部分。
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) {
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);
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;
这段代码看似很多,这里只说下关键的部分
下面是真正添加线程的部分,线程池中将线程包装成了一个Worker对象
w = new Worker(firstTask);
final Thread t = w.thread;
然后更新alivecount,这个数量是有线程池中的hashset来维护的
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
线程池中的线程执行
if (workerAdded) {
t.start();
workerStarted = true;
}
本次就到这吧,详情看下一篇java线程池中的Worker解析