线程是稀缺资源,如果线程被无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,所以合理的使用线程池对线程进行统一分配、调优和监控,有巨大的好处。
JDK1.5引入了Executor线程池框架,通过把任务的提交和执行进行解耦,我们只需要定义好任务(Runnable),然后将它提交给线程池,而不用关心该任务是如何执行、被哪个线程执行以及什么时候执行。
Java线程池相关类库全部位于java.util.concurrent包下,类结构如下图:
总共有四个,分别为
//1
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
//2
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
//3 ThreadFactory threadFactory)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
//4
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
构造函数主要是对线程池参数的初始化,关于线程池参数可以参考另外一篇文章Java线程池参数解析。其中构造函数1-3最后都是调用到了构造函数4,简单分析一下4即可。
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
由于corePoolSize、maximumPoolSize等线程池参数已经在博客Java线程池参数解析有过叙述,故此处不再赘述。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
ctl是线程池中一个非常重要的变量,以它的低29位表示线程池中处于RUNNING状态的线程个数,高3位表示线程池所处的状态(关于线程池的状态我们待会再讲)。
private static final int COUNT_BITS = Integer.SIZE - 3;
COUNT_BITS表示RUNNING状态下的线程数对应二进制的位数,也就是29。
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
CAPACITY用于与ctl做与运算,得到RUNNING状态下线程的个数。CAPACITY的值为
000111111111111111111111111111111
private static final int RUNNING = -1 << COUNT_BITS;
RUNNING表示线程池的运行状态,可处理新任务并执行队列中的任务,十进制表示为-536870912。二进制表示为
11100000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
SHOTDOWN表示线程池的关闭态,不接受新任务,但处理队列中的任务,值为0。
private static final int STOP = 1 << COUNT_BITS;
STOP表示线程池的停止态,不接受新任务,不处理队列中任务,且打断运行中任务,十进制表示为536870912。二进制表示为
00100000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
TIDYING表示线程池的整理态,所有任务已经结束,workerCount = 0 ,将执行terminated()方法,值为1073741824。二进制表示为
01000000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
TERMINATED表示线程池的结束态,terminated() 方法已完成,值为1610612736。二进制表示为
01100000000000000000000000000000
不难发现,在所有的五种状态中SHUTDOWN值等于0,RUNNING值小于0,其他三种状态STOP、TIDYING、TERMINATED值均大于0。
private final HashSet<Worker> workers = new HashSet<Worker>();
workers用于存储真正运行的任务Worker(后边我们会详细讲述Worker的运行原理)。
private volatile boolean allowCoreThreadTimeOut;
allowCoreThreadTimeOut用于表示核心线程在空闲一定时间之后是否过期。
为了更好的管理线程,Java线程池总共包含五种状态。要注意的是,线程池的状态要与线程的状态区别开来,这两者之间没有什么必然的联系。具体请看下图
了解线程池的五个状态,对于理解线程池的工作原理至关重要,它是基础中的基础,所以在读线程池源码前一定要了解一下它们。
接下来,我们主要分析一下execute()方法,也就是添加一个Runnable任务之后,到底发生了什么?
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);
}
首先,对添加的任务(也就是代码里边的command参数)判空,若空,则抛出NullPointerException异常。
然后,拿到ctl的值(也就是线程池运行状态(高3位)和当前运行任务数(低29位))。
然后,判断当前运行任务数(workerCountOf(c ))是否小于核心线程数(corePoolSize),简单看一下workerCountOf(c )方法。
private static int workerCountOf(int c) {
return c & CAPACITY;
}
CAPACITY我们在前边已经讲过,是低29位全1高3位全0,它与c做与运算得到的就是当前运行的线程个数。
然后执行addWorker()方法,具体请先看下文addWorker()方法。
如果addWorker()成功,则直接return,结束execute。
如果addWorker()失败,则刷新c的值。
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);
如果addWorker()失败,那么接下来的策略是将任务放入阻塞队列。
首先判断线程池状态是否为运行态isRunning©。
如果任务加入阻塞队列成功,即workQueue.offer(command)返回true。
接下来使用recheck做了一个二重检查,如果此时线程池已经处于非运行状态,则将任务remove掉。并且执行拒绝策略。
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
如果阻塞队列添加任务失败,则根据coreSize<当前线程数
然后,调用addWorker(command, true)方法向线程池添加任务,并以返回值作为判断条件,若添加成功,方法结束,return。若添加失败,则执行c = ctl.get() 更新c的值。我们看一下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.
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;
}
先看第一部分
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
}
}
首先定义了一个标签retry:,这是break的语法糖,用于跳出二重循环,有兴趣的小朋友可以研究一下哈。
然后以一个for (;; ) 开始了外层死循环(此处为什么不用while(true)而是用for(;; ) ,请参考for(;;)和while(true)的区别)。
先看前半部分
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;
变量c前边讲过,此处不赘述。变量rs表示的是什么呢?看一下runStateOf(c )方法实现
private static int runStateOf(int c) {
return c & ~CAPACITY;
}
由本文前边叙述我们知道CAPACITY值为
000111111111111111111111111111111
则~CAPACITY值为
11100000000000000000000000000000
c & ~CAPACITY则表示的是c的高三位表示的值,其实就是线程池的运行状态。
然后再看这段代码
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
}
此处也用了一个死循环。首先看wc变量,它表示当前线程池中正在运行的线程个数(前边讲过这个方法)。
先看第一个if判断
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
然后看第二个if判断
if (compareAndIncrementWorkerCount(c))
break retry;
如果compareAndIncrementWorkerCount(c )为真,则跳出外层死循环。看下compareAndIncrementWorkerCount(c )实现
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
ctl是AtomicInteger类型变量,此处采用了CAS操作保证了ctl自增的线程安全性。
如果compareAndIncrementWorkerCount(c )为假,说明ctl自增失败,原因有可能是其他线程做了自增操作。
所以需要更新变量c的值,并继续执行内层死循环做ctl自增操作。
但是在继续执行内层死循环之前,要先判断线程池的运行状态是否发生了变化,例如从RUNNING变成了STOP,则不能继续执行内层死循环,而是执行外层死循环,重新做状态判断。代码如下
if (runStateOf(c) != rs)
continue retry;
如果外层死循环执行结束,并且没有return false,那说明ctl自增操作成功。接下来开始添加新任务
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;
首先定义了两个boolean类型变量workerStarted表示线程是否已启动,workerAdded表示线程是否已经添加到线程池。Worker类型变量w表示此次要添加的线程,初始化为null。
接下来对代码添加异常检测try-finally,这个待会再说。首先看try块内代码。
w = new Worker(firstTask);
看到这,有必要了解一下Worker实例的初始化过程都做了哪些工作,请先看下文Worker实例初始化详细过程,然后在回到此处继续看。
接着,获取到了线程t,并对t判空。
然后获取全局可重入锁,并加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
接着,又加了一个try-finally,这个try没什么特殊含义,唯一的功能是释放可重入锁。
然后执行如下代码
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;
}
首先获取线程池运行状态rs,然后根据rs做逻辑判断。
如果线程池处于运行态或者处于关闭态但提交任务为空,则可以做添加线程操作。
判断线程是否已经启动,如果已经启动,抛出IllegalThreadStateException异常(此处为什么要检查线程状态,一直很费解,有理解的同学可以留言告知,谢谢)。
将Worker实例加入线程池workers。
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
largestPoolSize变量用于记录出现过的最大线程数。
置workerAdded变量为true,表示已经创建线程,并添加到了线程池。
然后执行如下代码
if (workerAdded) {
t.start();
workerStarted = true;
}
如果线程已经被加入线程池,则启动线程,并置标志位workerStarted为true,表示线程已经启动。
最后执行
if (! workerStarted)
addWorkerFailed(w);
如果线程没有启动成功,执行addWorkerFailed()开启回滚操作,具体请看下文 addWorkerFailed() 详细过程。
Worker实例添加成功之后。由于线程池把Worker作为线程来运行,所以有必要阅读一下Worker的run()方法,具体请看下文 Worker的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 ((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);
}
}
首先
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
获取当前线程wt,提交的任务task,并置w的firstTask为空。
然后w解锁。w.unlock()这行代码与之前w的构造函数的setState(-1)相对应。
接下来执行
while (task != null || (task = getTask()) != null)
如果提交的任务不为空,则处理提交的任务,如果提交的任务为空,则通过getTask()从阻塞队列获取任务。
我们先看一下 ,getTask()方法实现,具体请看下文 getTask()方法 详细过程。。
我们接着看。
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 {
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();
}
首先加全局锁,细节不管,最后执行
task.run();
相当于调用了我们提交的Runnable的run方法。
w.completedTasks++;用于计算所有已完成任务。
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
首先
boolean timedOut = false;
定义timedOut变量表示当前线程是否已经过期。
然后接一个死循环
for (;;)
然后
int c = ctl.get();
int rs = runStateOf(c);
获取到c和线程池状态。
然后
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
这个if的含义为 : 如果线程池处于STOP、TIDYING、TERMINATED三种状态之一,或者处于SHUTDOWN状态且阻塞队列为空时,执行decrementWorkerCount(),并返回空任务。
如果不是上述情景,则继续执行
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
获取当前运行的线程个数。
timed变量表示当前线程从阻塞队列获取Runnable时,是否执行过期这种操作,主要分为两种情况:1、是否允许核心线程过期;2、当前运行线程个数是否大于核心线程阈值。
然后
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
相当于如果满足wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty()),则将当前运行线程数减一,如果减一成功,返回null。如果减一失败,则继续往下执行。
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
首先根据是否执行过期策略(timed),以不同的方式(poll和take)从阻塞队列获取Runnable,如果获取任务r非空,则返回r。如果获取任务为空,则说明已经到了过期时间,timedOut = true,那么在下一次循环时,线程数减一,退出方法。
首先对w进行了初始化。我们首先看一下w的构造过程,以及Worker的类型。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread;
Runnable firstTask;
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
}
介于篇幅限制,此处只展示了部分代码。
我们发现,Worker实现了Runnable接口,表明Worker是一个作为线程来运行的。并且Worker继承了AbstractQueuedSynchronizer(AQS),关于AQS,我们在另外一个地方会有详细讨论,此处AQS主要用于终止工作中的线程。
首先,执行
setState(-1);
相当于调用父类AbstractQueuedSynchronizer的setState(int newState)方法
protected final void setState(int newState) {
state = newState;
}
即 将state值设置为-1,原因是防止线程被中断。
然后执行
this.firstTask = firstTask;
对成员变量firstTask赋值。此处参数值firstTask表示刚刚提交过来的Runnable任务,因为这个Worker线程以后有可能会消费阻塞队列中的任务,所以刚刚提交的任务被称为第一个任务。。
然后执行
this.thread = getThreadFactory().newThread(this);
注意 : 此处的入参是this,也就是当前Worker对象,即上文中的w对象。
再看一下newThread()方法,此处getThreadFactory()默认实例化DefaultThreadFactory对象(为什么是DefaultThreadFactory,请追踪构造方法)。newThread()代码如下
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
乖乖,你发现了什么?线程t竟然不是通过firstTask这个Runnable来构造的,而是通过Worker对象w来构造的。为什么这么设计呢?原因很简单,假如以firstTask来建立线程的话,那以后从阻塞队列再消费任务的时候,是不是读一个任务建一个线程呢?这样做的话,那也就失去了线程池的意义了啊。而已Worker实例来建立线程的话,完全可以在Worker线程的run()方法内调用任务的run()方法,实现线程的复用,实际上,线程池的研发人员就是这么做的。
讲到这,Worker实例w已经构造结束。
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
首先使用mainLock加锁。
然后,如果w不为空的话,从集合中remove掉该w。
接着,执行decrementWorkerCount()方法,decrementWorkerCount实现
private void decrementWorkerCount() {
do {
} while (! compareAndDecrementWorkerCount(ctl.get()));
}
这是一个死循环,当compareAndDecrementWorkerCount(ctl.get())为真的时候,才会褪出。看一下compareAndDecrementWorkerCount(ctl.get())方法实现
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
很明显,这是一个CAS操作,上边的do-while相当于自旋实现了一个CAS操作,最终实现ctl减一操作。
最后执行tryTerminate();tryTerminate()此处不细讲,有兴趣的小伙伴可以自行阅读源码哦!
下午接着写吧,先发出来,哈哈