ThreadPoolExecutor中的关键定义:
- Worker:内部类,继承了Runnable和AQS,定义了Thread线程对象。
- ctl:AtomicInteger类型对象,有两层含义:workerCount,表明当前有效的线程数;runState,表明当前线程池的状态,是否处于Running,Shutdown,Stop,Tidying,Terminate五种状态。
- workQueue:BlockingQueue<Runnable>类型,线程池的队列。
- mainLock:ReentrantLock类型,线程池的锁。
- workers:HashSet<Worker>类型,存放正在被执行的任务和线程。
一、主流程方法execute
使用executor.execute(worker) 来提交一个任务到线程池中去,这个方法非常重要,参数中什么时候创建核心线程数,什么时候创建最大线程数,就是在这里体现,下面我们来看看它的源码:
addWorker(null,false)的解释:
当任务添加到线程池阻塞队列成功后,重新检查一下现在线程池中是否有活跃线程。有可能存在这种情况,用户设置的核心线程数是0,在第1处时正好有1个线程在执行任务,所以第一处的if条件不成立,此时当前执行execute方法的线程被CPU调度为等待状态,当再次被调度执行时,刚刚线程池中的那个线程已经超过销毁时间而被销毁了。所以此时线程池中的线程数为0,如果不做recheck的话,那么这个任务有可能永远得不到执行。所以这是作者充分考虑了可能存在的异常情况,当新任务添加到队列后,重新检查一下线程池,如果没有活跃线程了,就执行addWorker方法。
二、Worker类解析
线程池中的线程,都会被封装成一个Worker类对象,ThreadPoolExecutor维护的其实就是一组Worker对象,其中用集合workers存储这些Worker对象;
Worker类中有两个属性,一个是firstTask,用来保存传入线程池中的任务,一个是thread,是在构造Worker对象的时候,利用ThreadFactory来创建的线程,用来处理任务的线程;
Worker继承AQS,使用AQS实现独占锁,并且是不可重入的,构造Worker对象的时候,会把锁资源状态设置成-1,因为新增的线程,还没有处理过任务,是不允许被中断的。
也就是说Worker对象本身就有lock()、unlock()、tryLock()、isLocked()等方法可以调用,用来给Worker对象加锁。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable{
....
Worker(Runnable firstTask) {
setState(-1); //AQS的方法,初始化线程状态
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);//工程创建线程
}
protected boolean isHeldExclusively() {
return getState() != 0;
}
public void run() { //线程池的执行方法。
runWorker(this);
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) { //AQS的方法,CAS设置状态
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); //AQS的方法,加锁 }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); //AQS的方法,释放锁 }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt(); //友好终止线程
} catch (SecurityException ignore) {
}
}
}
....
}
lock 方法一旦获取了独占锁,表示当前线程正在执行任务中;那么它会有以下几个作用
- 如果正在执行任务,则不应该中断线程;
- 之所以设置为不可重入,是因为我们不希望任务在调用像 setCorePoolSize 这样的线程池控制方法时重新获取锁,这样会中断正在运行的线程
- 线程池在执行 shutdown 方法或 tryTerminate 方法时会调用 interruptIdleWorkers 方法来中断空闲的线程,interruptIdleWorkers 方法会使用 tryLock 方法来判断线程池中的线程是否是空闲状态
- 如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断;
三、addWorker方法,创建线程的方法
addWorker这个方法主要用来创建新的工作线程,如果返回true说明创建和启动工作线程成功,否则的话返回的就是false。
参数:code为true时,表是创核心线程执行任务;为flase时,创建最大线程执行。
四、runWorker(Worker w)核心方法
runWorker是ThreadPoolExecutor的方法被内部类Worker中的run方法调用,线程执行任务的核心方法,创建对象时会被隐式执行。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
//释放锁,将state设置为0,允许中断任务的执行
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//如果任务不为空,或者从任务队列中获取的任务不为空,则执行while循环
while (task != null || (task = getTask()) != null) {
//如果任务不为空,则获取Worker工作线程的独占锁
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 {
//调用Runable接口的run方法执行任务
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;
//完成的任务数量加1
w.completedTasks++;
//释放工作线程获得的锁
w.unlock();
}
}
completedAbruptly = false;
} finally {
//执行退出Worker线程的逻辑
processWorkerExit(w, completedAbruptly);
}
}
五、getTask() 获取任务
getTask()被runWorker调用,是工作线程在while循环中获取任务队列中的任务对象的方法;
什么情况下getTask()返回null?
1.rs >= SHUTDOWN 成立: 当前的状态最低也是STOP状态,一定要返回 null了。
2.前置条件 状态是 SHUTDOWN, workQueue.isEmpty()
3.线程池中的线程数量,超过最大限制时,会有一部分线程返回null
4.线程池中的线程数超过 corePoolSize 时,会有一部分线程可能返回null
private Runnable getTask() {
// 表示当前线程获取任务是否超时; 默认 false, 未超时
boolean timedOut = false; // Did the last poll() time out?
// 自旋
for (;;) {
// 获取最新ctl值保存到c中
int c = ctl.get();
// 获取线程池当前运行状态
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 条件1: 条件成立:说明当前线程池是非RUNNING状态。
// 条件2: 2.1成立说明当前状态最低是STOP状态;
// 2.2 说明队列是null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 使用CAS + 死循环的方式让ctl的值 -1
decrementWorkerCount();
return null;
}
// 执行到这里,有几种情况?
// 1.线程池是RUNNING状态
// 2.线程池是SHUTDOWN状态 但是队列还未空,此时可以创建线程
// 获取线程池中的线程数量
int wc = workerCountOf(c);
// Are workers subject to culling?
// true: 表示当前这个线程 获取 task 时 是支持超时机制的,
// false: 表示当前线程不支持超时机制。 核心线程数量内的线程会使用 queue.take();
// 情况1: allowCoreThreadTimeOut == ture 表示核心线程数量内的线程,可以被回收; false不可以被回收
// wc > corePoolSize : 当前线程池中的数量时大于核心线程数的。
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 条件1:(wc > maximumPoolSize || (timed && timedOut): 为ture 不代表线程一定返回null
// 1.1 wc > maximumPoolSize : 可能外部线程将线程池最大线程数设置为比初始化时的要小
//1.2 timed && timedOut :当前线程使用poll方式获取task。 上一次循环时, 使用 pool方式获取任务时,超时了。
// 条件二: (wc > 1 || workQueue.isEmpty())
// 2.1: wc > 1: 说明当前线程池中,还有其他线程;当前线程可以直接回收,返回null。
// 2.2: 说明当前任务队列已经为null。 最后一个线程也可以放心退出
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 使用CAS方式进行退出当前线程,计数 -1;
if (compareAndDecrementWorkerCount(c))
return null;
//CAS失败,就继续;再次自旋时,timed可能为 false
continue;
}
try {
// 获取任务的逻辑
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
// 条件成立: 返回任务
if (r != null)
return r;
// 说明当前线程超时了。
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
六、processWorkerExit()方法来销毁工作线程
在runWorker(Worker w)方法finally中被调用,来销毁工作线程。该方法就是判断当前线程是需要将其删除还是继续执行任务。该方法也是ThreadPoolExecutor中的方法。
但这个方法只有在一定情况下才会起作用,如果已经从队列中取不出任务来了,或者在worker执行任务过程中出现了错误,这个方法就起到了作用,如果正常的话这个方法就没啥用。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
//异常退出,worker数量减一
//这个worker是正常工作的,但是现在没有任务了,要退出了,数量就不减1了?
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//汇总该worker(一个worker实际上就是一个线程)完成的任务数量到线程池
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
//退出woker的时候,检查下线程池状态
tryTerminate();
int c = ctl.get();
//线程池的状态是runing 或者 shutdown
if (runStateLessThan(c, STOP)) {
//是因为没有任务了才要终止这个线程的
if (!completedAbruptly) {
//假如allowCoreThreadTimeOut是false,那么线程池中的线程数量至少是corePoolSize
//假如allowCoreThreadTimeOut是true,那么一旦没有任务了,所有线程就都终止了
//这里有一个问题,因为正常退出的话,worker数量没有减1,所以即使worker数量满足要求,但worker线程却没有了,剩下的任务就没法执行了
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
//目前正在工作的线程数量大于等于min,则正常结束这个线程(就是维持线程数量至少是min)
if (workerCountOf(c) >= min)
return; // replacement not needed
}
//增加一个线程
addWorker(null, false);
}
}
七、interruptIdleWorkers()中断空闲线程
interruptIdleWorkers()方法的调用不是在本工作线程,而是在主线程中调用的。被shutdown()和shutdownNow(没有tryLock,直接暴力中断线程),tryTerminate()等调用。
//尝试关闭空闲的线程。
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock(); // 主线程加锁。
try {
//循环遍历workers,尝试获取锁tryLock,如果成功,执行中断操作interrupt
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();
}
}
抛出问题:
怎么添加到队列里面的?
从队列里面获取任务后怎么选择线程执行的?