第三篇了,其实关于多线程这边的话,我想一直分下去,每一篇都研究一点,不太想将所有的知识点都放在一起,放在一起,读起来太费力了,看起来也不舒服,分下来之后至少一目了然,一个个知识点很清晰。
今天的话就来看看它的一个内部类worker,这个worker应该算是核心了。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable{
//此处说明下worker中的锁是不可重入的,可以去看下它获取锁的逻辑,很清晰
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
//当前worker所关联的对象,需要的时候也是直接获取即可
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
//线程创建后的初始任务
/** Initial task to run. Possibly null. */
Runnable firstTask;
//线程完成的任务数量,注意这边是volatile类型的
/** 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) {
//worker的初始化
//至于这边为什么会将state设置成-1,jdk这边是有注释的
//inhibit interrupts until runWorker ,防止它在执行前被中断,至于中断时是怎么判断的,待会儿再看
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);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
//状态不为0说明某个线程获取到worker锁
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
//此处就能看见和ReentrantLock不一样,如果是0的话,说明其他线程没有获取到锁,此时CAS设置成1
if (compareAndSetState(0, 1)) {
//将拥有者设置成当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
//如果此处是同一线程进入的话,一样返回fasle,表示同一个线程再次进入时不能获取到锁的
return false;
}
protected boolean tryRelease(int unused) {
//释放线程操作,将状态设置成0,返回true
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
//中断线程,如果此线程启动了的话
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
//运行之前设置状态为0,也就意味着这边可以执行中断了。
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//如果task不为null,说明是刚刚创建出来的,热乎乎的线程,第二篇里面也分析了,这边的条件应该是能够直接创建线程的
//如果task为null,说明什么?说明此时没有能够创建出线程,那么此时就需要从队列中拿了,因此紧接着就是去队列获取
//getTask稍后分析,这边先主动忽略
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
//这边的判断比较复杂,不急,慢慢看,简化下来就是如下
//( (1 || (2 && 3)) && 4)
//1. 先看条件1,条件1就是在判断当前线程池的状态,是否是STOP及以上,是的,返回true,否返回false.此处就是判断出当线程池的状态
//2. 看条件2,interrupted(),需要知道这个方法并不能去中断一个线程,只是返回当前中断状态并给清除当前状态设置成false
//2.1 此时就有两种状态,假设当前线程的中断状态本就是false的话,此处返回,那么条件就变成((false || (false && 3) && 4),为什么此处1处是flase呢?因为只有
//flase才会到 2,3的判断,||运算符具有短路效果,因此此时就会整个跳出if逻辑段了,那么此时条件1的false是什么呢?说明线程池的状态是RUNNING或者SHUTDOWN,此时
//不应该被中断的。
//2.2 此时返回的即使true,就意味着当前线程被中断过了,那么就需要再次判断线程池的状态是不是由于shutdownNow 造成的,因为shutdownNow 存在并发的问题,会修改
//为STOP,如果是的话,返回true
//此时条件状态为可能为(( true ) && 4)或者( false || (true && true) && 4),需要注意3位置处的状态,有可能在条件1处的时候还是false,但是由于并发的关系,此时
//修改成STOP了,因此,就会进入到条件4的判断.
//4. isInterrupted方法可以看见穿参为false,意味着直接返回线程状态, 如果线程没有被中断,也就是 4返回的是false,那么进入中断流程,如果中断了,拉倒,跳出循环
if ( ( runStateAtLeast(ctl.get(), STOP)//1 ||
(Thread.interrupted() && //2
runStateAtLeast(ctl.get(), STOP)//3
)
) && !wt.isInterrupted() //4
)
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);
}
}
private Runnable getTask() {
//这个注释不知道是谁的,不过写在这边莫名戳中我笑点。。。。。。
boolean timedOut = false; // Did the last poll() time out?
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//遇到分支我么都需要好好分析,这个才是去揣摩作者意图最好的地方
//if条件要进来,rs必须比SHUTDOWN大
//1.如果rs = SHUTDOWN状态,那么 rs != STOP ,此时去判断workQueue是否是空,如果是空,那么久要将线程数量减1,减掉的其实就是当前线程了,这样在外层当前线程就结束了
//2.如果 rs > SHUTDOWN,那么rs起码是STOP了,此时|| 具有短路效果,不在获取队列任务,直接返回null
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
boolean timed; // Are workers subject to culling?
for (;;) {
int wc = workerCountOf(c);
//来看看这个标志位的意义,首先,上一篇博客提到过allowCoreThreadTimeOut,设置为true的话,是允许线程有超时时间的
//1.因此allowCoreThreadTimeOut 如果是true,那么timed就是true,也就意味着从队列中获取时也是需要超时的
//2.如果allowCoreThreadTimeOut是false,这边和corePoolSize(核心线程数)作比较了,想想意义是什么?
//如果当前线程数小于核心线程数,那么说明,此时线程池内只有核心线程数在运行,此时allowCoreThreadTimeOut为false,也就
//意味着即使线程池内线程空闲,也不受超时
//如果wc > corePoolSize ,就意味着此时有非核心线程在运行,还记得是什么时候出现的?上一篇将拒绝策略时试验过,超过核心线程但是没到
//最大线程数时,并且队列已满,此时新起线程运行,在这种情况下,需要设置timed为true
timed = allowCoreThreadTimeOut || wc > corePoolSize; // 1
if (wc <= maximumPoolSize && ! (timedOut && timed)) // 2
//注意此处timedOut一开始就是false,因此在首次进入for循环时如果wc并没有超过最大线程数的话 会直接跳出,继续从下面逻辑中取出线程
break; //3
if (compareAndDecrementWorkerCount(c))
//如果很不幸的进入此的话,这边也是个CAS操作,即将数量减1,返回null出去,这边返回null并没有关系,外层依然是个无线循环
return null; //4
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry; //这边说的很明显了,状态不一致,因此当前线程执行失败,所以重来
// else CAS failed due to workerCount change; retry inner loop
}
try {
//正常情况下 timed都是false,不需要考虑超时,因此都是直接去workQueue中取,这边我们以LinkBlockQueue为例去分析
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
//注意此处设置了timeOut为 true,也就是在从队列中并没有成功取出任务,r == null 了,这边设置了timeOut为true
//这边注意下上面标注的1 和 2 的地方
//如果1 处的 allowCoreThreadTimeOut一开始就是设置成true的话,那么在首次循环结束,再次循环到2时,此时timeOut和timed都是true,3处执行不到,就会
//执行到4处返回null,因为之所以能循环到2,也就是之前一次循环没有取出任务
//如果1处的allowCoreThreadTimeOut就是设置的false,设想一下wc如果一直没有超过核心线程数,那么break逻辑永远都会执行,直至线程池状态变成SHUTDOWN或者以上,才可能返回null出去
//那么如果wc超过了呢?timed就是true了,那么此时wc如果处在maximumPoolSize以内,那么break也不会执行
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
//LinkBlockQueue的take方法,poll的操作差不多,只不过是多了超时时间
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
//这边可以理解为生产者消费者的实现哦,当count内没有线程时,及await,等待,生产者生产线程
notEmpty.await();
}
x = dequeue(); // 出对操作,具体细节不看
c = count.getAndDecrement(); //返回当前值数量
if (c > 1)
notEmpty.signal(); //如果取出之后队列中依然有元素,唤醒notEmpty,继续取
} finally {
takeLock.unlock();
}
if (c == capacity) //这边之前也分析过为什么这样,因为如果c是等于capacity的话,那么此次取出之后,队列中实际上是可以继续生产的,因此唤醒生产者线程
signalNotFull();
return x;
}
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//此处可以推断一下,就是判断当前调用线程是否有权限去执行SHUTDOWN操作,不是我们关注的重点
checkShutdownAccess();
advanceRunState(SHUTDOWN); //将线程池状态设置成SHUTDOWN,此时也就表示不支持接收新任务了
interruptIdleWorkers(); //中断所有空闲线程
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
private void advanceRunState(int targetState) {
for (;;) {
//CAS操作
int c = ctl.get(); //获取状态位
//看看条件1处,如果c的状态位大于SHUTDOWN,假设为STOP吧,直接break掉,因为此时线程池状态其实已经超过SHUTDOWN了,不需要改了
//如果条件1为 false的话,也是采用CAS将ctl处设置SHUTDOWN和数量,成功后直接break,如果失败了,一直循环
if (runStateAtLeast(c, targetState) //1
||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c)))) //2
break;
}
}
private void interruptIdleWorkers(false) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) { //此处为什么需要对w尝试去获取锁呢?因为worker在执行时,是需要获取锁的,也就意味着正在运行的线程是不能中断的
try {
t.interrupt(); //将空闲线程中断标志位设置成 true
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
//想想为什么此处会有这个方法????因为可能会有一种情况,线程池内的空闲线程进队列拿任务时,碰巧没有了,被之前的线程拿走了,并且此时队列任务为空了,因此会全部阻塞在take方法处.......,但是
//此时线程池的状态是SHUTDOWN,又不允许新任务进入,这可咋办???? 那么这边就提供了tryTerminate 这个方法,尝试去中断
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
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 {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}