之前测试CountDownLatch时,使用了线程池。
ExecutorService service = Executors.newFixedThreadPool(3);
service.submit(run);
service.shutdown();
基于此,今天来看看java给我们提供的线程池有哪些?作为码农,不能以会使用作为终极目标,了解源码觉得很有必要,通过源码,可以知道它的执行原理,可以知道哪些可为,哪些不可为,可以说这是内功,不是一两天就能就能练成的,而招式就是你的使用,没有内功的支持,即使你的招式再华丽,也没有威力,说不定一个小小的问题,就能威胁你的生命~~~~~~~~~~~~。所以内功和招式相辅相成,武侠里,有些人内功深厚,和谁比试都不会吃亏,单纯的招式不够看。。。题外话了。
翻开源码,可以看见java给我们提供的线程池 Executor,它是顶级接口,里面只有一个execute方法,所以它只是一个执行工具,真正的线程池接口是ExecutorService,作为面向接口编程的理解,那么它一定是指向它的实现类,它的实现类是由Executors里面的静态方法生成的。
翻看Executors没的静态方法,发现它们构建的对象其实是ThreadPoolExecutor,那么今天的主角就出来了,先来看看它吧,之前分析过队列的一些知识,CAS,看起来就不会费力了。
先来理解一下它里面的一些变量,对于下面的逻辑段理解有好处,内部使用了大量的位移操作,需要慢慢理解。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//COUNT_BITS允许的最大线程数所占的位数 32-3 = 29,最低就是0位,最大是29位
private static final int COUNT_BITS = Integer.SIZE - 3;
//CAPACITY 允许的最大线程数,1 * 2^29 - 1
//00000000 00000000 00000000 00000001 其实也就是 1左移 29 位
//00100000 00000000 00000000 00000000 再减去 1
//000 11111 11111111 11111111 11111111
//前面三位是状态位,后面29位是线程数目位(为什么会这么分是因为AtomicInteger这个复合型变量)
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
//-1在计算机内部是32个1
//11111111 11111111 11111111 11111111 左移29位,
//111 00000 00000000 00000000 00000000 那么这边可以看见状态位其实是由高3位决定的,RUNNING的状态是 111
//该状态下线程池能接受新任务,并且可以运行队列中的任务
private static final int RUNNING = -1 << COUNT_BITS;
//00000000 00000000 00000000 00000000
//000 00000 00000000 00000000 00000000 状态为 000
//该状态下的线程池不再接受新任务,但仍可以执行队列中的任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
//00000000 00000000 00000000 00000001
//001 00000 00000000 00000000 00000000 状态为 001
//以下状态下的线程池不再接受新任务,不再执行队列中的任务,而且要中断正在处理的任务
private static final int STOP = 1 << COUNT_BITS;
//00000000 00000000 00000000 00000010
//010 00000 00000000 00000000 00000000 状态为 010
private static final int TIDYING = 2 << COUNT_BITS;
//00000000 00000000 00000000 00000011
//011 00000 00000000 00000000 00000000 状态为 011
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
// CAPACITY 为 000 11111 11111111 11111111 11111111
// 取非操作 为 111 00000 00000000 00000000 00000000,这样的话再和c进行 与 操作,那么高3位的值就能原样保留,低29位置成0,那么c里面低29位全部为0
private static int runStateOf(int c) { return c & ~CAPACITY; }
// CAPACITY 为 000 11111 11111111 11111111 11111111
// 获取workerCount,workerCount是低29位的值,高3位为0,不管.
private static int workerCountOf(int c) { return c & CAPACITY; }
//传入的rs表示线程池运行状态runState,其是高3位有值,低29位全部为0的int,
//而wc则代表线程池中有效线程的数量workerCount,其为高3位全部为0,而低29位有值得int,
//将runState和workerCount做或操作|处理,即用runState的高3位,workerCount的低29位填充的数字,
//而默认传入的runState、workerCount分别为RUNNING和0。
private static int ctlOf(int rs, int wc) { return rs | wc; }
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)) //关注此处,加入Worker
return;
c = ctl.get(); //失败之后尝试再次加入
}
if (isRunning(c) && workQueue.offer(command)) { //workQueue为新增线程的队列
int recheck = ctl.get(); //成功加入之后再次检查状态
if (! isRunning(recheck) && remove(command))
reject(command); //如果状态不一致,remove掉
else if (workerCountOf(recheck) == 0)
addWorker(null, false); //自增空任务
}
else if (!addWorker(command, false))
reject(command); //直接reject掉
}
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()))
//1. 当状态超过或者就是SHUTDOWN时,不再接收新进的线程
//2. 当状态为SHUTDOWN时, ! (rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()) 这个状态有点复杂
//我们来看 需要 整个状态是 真 才能return false。
//因此 rs == SHUTDOWN && firstTask == null && workQueue.isEmpty() 这个状态需要为假才行
//那么在 rs 是 SHUTDOWN 的情况下 firstTask == null()为假,或者 !workQueue.isEmpty()为假就行
//2.1 firstTask == null()为假 ,就是当前任务存在
//2.2 workQueue.isEmpty()为真 ,任务队列空的
//就是当状态是SHUTDOWN时,当前任务存在及 任务队列为空或者任务队列有任务时,不允许添加进来
return false;
for (;;) {
//获取当前数量
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
//大于允许的最大值,也是不允许添加进来
return false;
//如果允许添加,CAS操作将Count+1
if (compareAndIncrementWorkerCount(c))
break retry; //跳出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; //Work是个内部类,暂时不分析结构,新建处的线程都是加入到Worker内
try {
final ReentrantLock mainLock = this.mainLock;
w = new Worker(firstTask); //new出Worker,内部将状态设置成-1,继承自AQS,因此关联到AQS内去分析-1状态
final Thread t = w.thread; //获取内部创建的thread,也是通过将Runnable封装到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)) {
//1.这个地方其实就是在说在Running态下能新增
//2.或者在SHUTDOWN下但是新增任务是空的时候能进入,SHUTDOWN状态是不能新增任务的,但是需要执行线程池内剩余的任务
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w); //加入workers
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s; //记录最大线程数
workerAdded = true; //将标志记录为true
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); //如果添加进入了,启动
workerStarted = true; //启动标志设置为true
}
}
} finally {
if (! workerStarted)
//如果启动失败立即回退,从workers中删除,尝试结束线程
addWorkerFailed(w);
}
return workerStarted;
}
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w); //remove
decrementWorkerCount(); //CAS减1
tryTerminate(); ///尝试结束
} finally {
mainLock.unlock();
}
}
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
//1. isRunning状态下直接返回
//2. c 状态 >=2 时,忽略
//3. 如果是SHUTDOWN时,只要队列任务不空,就返回不做操作
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE); //尝试去中断一个空闲线程。
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
//如果此时线程数量是0 了,就将状态设置成TERMINATED
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll(); //通知其他等待的线程
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) { //注意此处,执行任务的线程是互斥的锁定对象,因此这边如果能获取到锁的话,可以认定是空闲线程
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
本篇文章大致分析了一下execute的方法,从整个方法入手我们可以直观的看见一些东西,那我们来总结一下ThreadPoolExecutor的一些流程。
1.如果当前线程池有效线程数目小于corePoolSize的话,corePoolSize就是我们在构建时传入的允许的线程池的大小(包括正在运行的和空闲的线程数目)。那么就尝试添加新的worker线程处理command,调用有效线程数组时workerCountOf()方法,第二个参数true是作为边界值的约束条件使用,成功添加直接返回,否则再次获取clt的值c,也就是状态。
2.根据c判断出当前线程池是否为RUNNING状态。即既能接收新任务,又能处理队列中任务的状态,此时的c是重新获取的状态,多线程的处理是比较复杂的。通过offer方法加入到队列中,如果成功添加进入了队列中。那么需要再次获取状态,检查当前线程池的状态是不是RUNNING,这边再次做检查的目的,也就是为了判断出当做过一次添加线程进入队列之后,需要保证此时线程池任然处于一个有效状态。如果不是RUNNING状态的话,从队列中移除刚刚添加的线程,并且将当前任务拒绝掉。如果失败了,那么就在当前线程池中的数目是0时添加一个worker线程,但是不携带任务。
3.最后的逻辑在什么条件都不满足时,强行去新增一个线程处理任务,但是还是失败了直接拒绝掉。
整体理解的话不是很难啊,下一篇重点来分析内部的一些构造和一些具体的方法