aqs全称为AbstractQueuedSynchronizer,它提供了一个FIFO队列,能够当作是一个用来实现同步锁以及其余涉及到同步功能的核心组件,常见的有:ReentrantLock、CountDownLatch等。
AQS是一个抽象类,主要是经过继承的方式来使用,它自己没有实现任何的同步接口,仅仅是定义了同步状态的获取以及释放的方法来提供自定义的同步组件。
能够这么说,只要搞懂了AQS,那么J.U.C中绝大部分的api都能轻松掌握。html
从使用层面来讲,AQS的功能分为两种:独占和共享java
仍然以ReentrantLock为例,来分析AQS在重入锁中的使用。毕竟单纯分析AQS没有太多的含义。先理解这个类图,能够方便咱们理解AQS的原理
node
AQS的实现依赖内部的同步队列,也就是FIFO的双向队列,若是当前线程竞争锁失败,那么AQS会把当前线程以及等待状态信息构形成一个Node加入到同步队列中,同时再阻塞该线程。当获取锁的线程释放锁之后,会从队列中唤醒一个阻塞的节点(线程)。
面试
AQS队列内部维护的是一个FIFO的双向链表,这种结构的特色是每一个数据结构都有两个指针,分别指向直接的后继节点和直接前驱节点。因此双向链表能够从任意一个节点开始很方便的访问前驱和后继。每一个Node实际上是由线程封装,当线程争抢锁失败后会封装成Node加入到ASQ队列中去编程
当出现锁竞争以及释放锁的时候,AQS同步队列中的节点会发生变化,首先看一下添加节点的场景。
这里会涉及到两个变化segmentfault
head节点表示获取锁成功的节点,当头结点在释放同步状态时,会唤醒后继节点,若是后继节点得到锁成功,会把本身设置为头结点,节点的变化过程以下
这个过程也是涉及到两个变化api
这里有一个小的变化,就是设置head节点不须要用CAS,缘由是设置head节点是由得到锁的线程来完成的,而同步锁只能由一个线程得到,因此不须要CAS保证,只须要把head节点设置为原首节点的后继节点,而且断开原head节点的next引用便可数据结构
一、CountDownLatch,简单大体意思为:A组线程等待另外B组线程,B组线程执行完了,A组线程才能够执行; state初始化假设为N,后续每countDown()一次,state会CAS减1。 等到全部子线程都执行完后(即state=0),会unpark()主调用线程,而后主调用线程就会从await()函数返回,继续后余动做。 二、ReentrantLock,简单大体意思为:独占式锁的类; state初始化为0,表示未锁定状态,而后每lock()时调用tryAcquire()使state加1, 其余线程再tryAcquire()时就会失败,直到A线程unlock()到state=0(即释放锁)为止,其它线程才有机会获取该锁; 三、Semaphore,简单大体意思为:A、B、C、D线程同时争抢资源,目前卡槽大小为2,若A、B正在执行且未执行完,那么C、D线程在门外等着,一旦A、B有1个执行完了,那么C、D就会竞争看谁先执行; state初始值假设为N,后续每tryAcquire()一次,state会CAS减1,当state为0时其它线程处于等待状态, 直到state>0且<N后,进程又能够获取到锁进行各自操做了;
一、protected boolean isHeldExclusively() // 须要被子类实现的方法,调用该方法的线程是否持有独占锁,通常用到了condition的时候才须要实现此方法 二、protected boolean tryAcquire(int arg) // 须要被子类实现的方法,独占方式尝试获取锁,获取锁成功后返回true,获取锁失败后返回false 三、protected boolean tryRelease(int arg) // 须要被子类实现的方法,独占方式尝试释放锁,释放锁成功后返回true,释放锁失败后返回false 四、protected int tryAcquireShared(int arg) // 须要被子类实现的方法,共享方式尝试获取锁,获取锁成功后返回正数1,获取锁失败后返回负数-1 五、protected boolean tryReleaseShared(int arg) // 须要被子类实现的方法,共享方式尝试释放锁,释放锁成功后返回正数1,释放锁失败后返回负数-1 六、final boolean acquireQueued(final Node node, int arg) // 对于进入队尾的结点,检测本身能够休息了,若是能够修改则进入SIGNAL状态且进入park()阻塞状态 七、private Node addWaiter(Node mode) // 添加结点到链表队尾 八、private Node enq(final Node node) // 若是addWaiter尝试添加队尾失败,则再次调用enq此方法自旋将结点加入队尾 九、private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) // 检测结点状态,若是能够休息的话则设置waitStatus=SIGNAL并调用LockSupport.park休息; 十、private void unparkSuccessor(Node node) // 释放锁时,该方法须要负责唤醒后继节点
一、获取独占锁: public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } acquire{ 若是尝试获取独占锁失败的话( 尝试获取独占锁的各类方式由AQS的子类实现 ), 那么就新增独占锁结点经过自旋操做加入到队列中,而且根据结点中的waitStatus来决定是否调用LockSupport.park进行休息 } 二、释放独占锁: public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; } release{ 若是尝试释放独占锁成功的话( 尝试释放独占锁的各类方式由AQS的子类实现 ), 那么取出头结点并根据结点waitStatus来决定是否有义务唤醒其后继结点 } 三、获取共享锁: public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) doAcquireShared(arg); } acquireShared{ 若是尝试获取共享锁失败的话( 尝试获取共享锁的各类方式由AQS的子类实现 ), 那么新增共享锁结点经过自旋操做加入到队尾中,而且根据结点中的waitStatus来决定是否调用LockSupport.park进行休息 } 四、释放共享锁: public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } releaseShared{ 若是尝试释放共享锁失败的话( 尝试释放共享锁的各类方式由AQS的子类实现 ), 那么经过自旋操做唤完成阻塞线程的唤起操做 }
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { private static final long serialVersionUID = 7373984972572414691L; /** * 同步队列头节点 */ private transient volatile Node head; /** * 同步队列尾节点 */ private transient volatile Node tail; /** * 同步状态 */ private volatile int state; /** * 状态在内存中的偏移位置 */ private static final long stateOffset; /** * 同步队列头节点在内存中的偏移位置 */ private static final long headOffset; /** * 同步队列尾节点在内存中的偏移位置 */ private static final long tailOffset; /** * 节点等待状态在内存中的偏移位置 */ private static final long waitStatusOffset; /** * 节点next节点在内存中的偏移位置 */ private static final long nextOffset; static { try { stateOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("state")); headOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("head")); tailOffset = unsafe.objectFieldOffset (AbstractQueuedSynchronizer.class.getDeclaredField("tail")); waitStatusOffset = unsafe.objectFieldOffset (Node.class.getDeclaredField("waitStatus")); nextOffset = unsafe.objectFieldOffset (Node.class.getDeclaredField("next")); } catch (Exception ex) { throw new Error(ex); } } /** * 使用CAS 初始化同步对了头节点 */ private final boolean compareAndSetHead(Node update) { return unsafe.compareAndSwapObject(this, headOffset, null, update); } /** * 使用CAS,更新同步队列的头节点 */ private final boolean compareAndSetTail(Node expect, Node update) { return unsafe.compareAndSwapObject(this, tailOffset, expect, update); } /** * 使用CAS 更新节点中等待状态waitStatus */ private static final boolean compareAndSetWaitStatus(Node node, int expect, int update) { return unsafe.compareAndSwapInt(node, waitStatusOffset, expect, update); } /** * 使用CAS 更新节点中next */ private static final boolean compareAndSetNext(Node node, Node expect, Node update) { return unsafe.compareAndSwapObject(node, nextOffset, expect, update); } ....省略
AQS 内部提供了一个内部类.用来做为同步队列和等待队列的节点对象.
不一样队列的节点.其使用的属性和含义是不一样的多线程
static final class Node { /** 共享 */ static final Node SHARED = new Node(); /** 独占 */ static final Node EXCLUSIVE = null; /** * 双向同步队列节点时使用,由于超时或者中断,节点会被设置为取消状态,被取消的节点时不会参与到竞争中的,他会一直保持取消状态不会转变为其余状态; */ static final int CANCELLED = 1; /** * 双向同步队列节点时使用,后继节点的线程处于等待状态. * 而当前节点的线程若是释放了同步状态或者被取消,将会通知后继节点,使后继节点的线程得以运行 */ static final int SIGNAL = -1; /** * 单向等待队列节点时使用,等待节点须要被唤醒 */ static final int CONDITION = -2; /** * 双向同步队列节点时使用,共享模式释放时,会将节点设置为此状态,并一直传播通知后续节点中止阻塞。尝试获取锁。 */ static final int PROPAGATE = -3; /** 等待状态 */ volatile int waitStatus; /** 双向同步队列节点时使用,前置节点指针 */ volatile Node prev; /** 双向同步队列节点时使用,后置节点指针 */ volatile Node next; /** 获取同步状态的线程 */ volatile Thread thread; /** 单项等待队列节点时使用,后置节点指针**/ Node nextWaiter; //是否时CLH队列的节点同时时共享式获取同步状态 final boolean isShared() { return nextWaiter == SHARED; } //获取当前节点的前置节点 final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { } //建立同步队列节点,node传入Node.SHARED或Node.EXCLUSIVE Node(Thread thread, Node mode) { this.nextWaiter = mode; this.thread = thread; } //建立等待队列节点,waitStatus传入Node.CONDITION Node(Thread thread, int waitStatus) { this.waitStatus = waitStatus; this.thread = thread; } }
一、构造器源码: /** * Creates an instance of {@code ReentrantLock}. * This is equivalent to using {@code ReentrantLock(false)}. */ public ReentrantLock() { sync = new NonfairSync(); } /** * Creates an instance of {@code ReentrantLock} with the * given fairness policy. * * @param fair {@code true} if this lock should use a fair ordering policy */ public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } 二、默认构造方法为非公平锁,带参构造方法还可经过传入变量还决定调用方是使用公平锁仍是非公平锁;
一、AQS --> Sync ---> FairSync // 公平锁 | |> NonfairSync // 非公平锁 二、ReentrantLock内的同步器都是经过Sync抽象接口来操做调用关系的,细看会发现基本上都是经过sync.xxx之类的这种调用方式的;
一、源码: public void lock() { sync.lock(); } // FairSync 公平锁调用方式 final void lock() { acquire(1); // 尝试获取独占锁 } // NonfairSync 非公平锁调用方式 final void lock() { if (compareAndSetState(0, 1)) // 首先判断state资源是否为0,若是恰巧为0则代表目前没有线程占用锁,则利用CAS占有锁 setExclusiveOwnerThread(Thread.currentThread()); // 当独占锁以后则将设置exclusiveOwnerThread为当前线程 else acquire(1); // 若CAS占用锁失败的话,则再尝试获取独占锁 } 二、这里的区别就是非公平锁在调用lock时首先检测了是否经过CAS获取锁,发现锁一旦空着的话,则抢先一步占为己有, 无论有没有阻塞队列,只要当前线程来的时候发现state资源没被占用那么当前线程就抢先一步试一下CAS,CAS失败了它才去排队;
一、源码: public final void acquire(int arg) { if (!tryAcquire(arg) && // 尝试获取锁资源,若获取到资源的话则线程直接返回,此方法由AQS的具体子类实现 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 不然获取资源失败的话,那么就进入等待队列 selfInterrupt(); } 二、该方法是独占模式下线程获取state共享资源的入口,若是获取到资源的话就返回,不然建立独占模式结点加入阻塞队列,直到获取到共享资源; 三、并且这里须要加上自我中断判断,主要是由于线程在等待过程当中被中断的话,它是不响应的,那么就只有等到线程获取到资源后经过自我判断将这个判断后续补上; 四、独占模式的该方法,正常状况下只要没有获取到锁,该方法一直处于阻塞状态,获取到了则跳出该方法区;
一、公平锁tryAcquire源码: // FairSync 公平锁的 tryAcquire 方法 protected final boolean tryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 获取锁资源的最新内存值 if (c == 0) { // 当state=0,说明锁资源目前尚未被任何线程被占用 if (!hasQueuedPredecessors() && // 检查线程是否有阻塞队列 compareAndSetState(0, acquires)) { // 若是没有阻塞队列,则经过CAS操做获取锁资源 setExclusiveOwnerThread(current); // 没有阻塞队列,且CAS又成功获取锁资源,则设置独占线程对象为当前线程 return true; // 返回标志,告诉上层该线程已经获取到了锁资源 } } // 执行到此,锁资源值不为0,说明已经有线程正在占用这锁资源 else if (current == getExclusiveOwnerThread()) { // 既然锁已经被占用,则看看占用锁的线程是否是当前线程 int nextc = c + acquires; // 若是占用的锁的线程是当前线程的话,则为重入锁概念,状态值作加1操做 // int类型值小于0,是由于该int类型的state状态值溢出了,溢出了的话那得说明这个锁有多难获取啊,可能出问题了 if (nextc < 0) throw new Error("Maximum lock count exceeded"); setState(nextc); return true; // 返回成功标志,告诉上层该线程已经获取到了锁资源 } return false; // 返回失败标志,告诉上层该线程没有获取到锁资源 } 二、非公平锁tryAcquire源码: // NonfairSync 非公平锁的 tryAcquire 方法 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); // 调用父类的非公平获取锁资源方法 } // NonfairSync 非公平锁父类 Sync 类的 nonfairTryAcquire 方法 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 获取锁资源的最新内存值 if (c == 0) { // 当state=0,说明锁资源目前尚未被任何线程被占用 if (compareAndSetState(0, acquires)) { // 先无论三七二十一,先尝试经过CAS操做获取锁资源 setExclusiveOwnerThread(current); // CAS一旦成功获取锁资源,则设置独占线程对象为当前线程 return true;// 返回成功标志,告诉上层该线程已经获取到了锁资源 } } // 执行到此,锁资源值不为0,说明已经有线程正在占用这锁资源 else if (current == getExclusiveOwnerThread()) { // 既然锁已经被占用,则看看占用锁的线程是否是当前线程 int nextc = c + acquires; // 若是占用的锁的线程是当前线程的话,则为重入锁概念,状态值作加1操做 // int类型值小于0,是由于该int类型的state状态值溢出了,溢出了的话那得说明这个锁有多难获取啊,可能出问题了 if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); // return true; // 返回成功标志,告诉上层该线程已经获取到了锁资源 } return false; // 返回失败标志,告诉上层该线程没有获取到锁资源 } 三、tryAcquire方法是AQS的子类实现的,也就是ReentrantLock的两个静态内部类实现的,目的就是经过CAS尝试获取锁资源, 获取锁资源成功则返回true,获取锁资源失败则返回false;
一、源码: /** * Creates and enqueues node for current thread and given mode. * * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared * @return the new node */ private Node addWaiter(Node mode) { // 按照给定的mode模式建立新的结点,模式有两种:Node.EXCLUSIVE独占模式、Node.SHARED共享模式; Node node = new Node(Thread.currentThread(), mode); // Try the fast path of enq; backup to full enq on failure Node pred = tail; // 将先队尾结点赋值给临时变量 if (pred != null) { // 若是pred不为空,说明该队列已经有结点了 node.prev = pred; if (compareAndSetTail(pred, node)) { // 经过CAS尝试将node结点设置为队尾结点 pred.next = node; return node; } } // 执行到此,说明队尾没有元素,则进入自旋首先设置头结点,而后将此新建结点添加到队尾 enq(node); // 进入自旋添加node结点 return node; } 二、 addWaiter经过传入不一样的模式来建立新的结点尝试加入到队列尾部,若是因为并发致使添加结点到队尾失败的话那么就进入自旋将结点加入队尾;
一、源码: /** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ private Node enq(final Node node) { for (;;) { // 自旋的死循环操做方式 Node t = tail; // 由于是自旋方式,首次链表队列tail确定为空,可是后续链表有数据后就不会为空了 if (t == null) { // Must initialize if (compareAndSetHead(new Node())) // 队列为空时,则建立一个空对象结点做为头结点,无心思,可认为傀儡结点 tail = head; // 空队列的话,头尾都指向同一个对象 } else { // 进入 else 方法里面,说明链表队列已经有结点了 node.prev = t; // 由于存在并发操做,经过CAS尝试将新加入的node结点设置为队尾结点 if (compareAndSetTail(t, node)) { // 若是node设置队尾结点成功,则将以前的旧的对象尾结点t的后继结点指向node,node的前驱结点也设置为t t.next = node; return t; } } // 若是执行到这里,说明上述两个CAS操做任何一个失败的话,该方法是不会放弃的,由于是自旋操做,再次循环继续入队 } } 二、enq经过自旋这种死循环的操做方式,来确保结点正确的添加到队列尾部,经过CAS操做若是头部为空则添加傀儡空结点,而后在循环添加队尾结点;
一、源码: /** * CAS head field. Used only by enq. */ private final boolean compareAndSetHead(Node update) { return unsafe.compareAndSwapObject(this, headOffset, null, update); } /** * CAS tail field. Used only by enq. */ private final boolean compareAndSetTail(Node expect, Node update) { return unsafe.compareAndSwapObject(this, tailOffset, expect, update); } 二、CAS操做,设置头结点、尾结点;
一、源码: /** * Acquires in exclusive uninterruptible mode for thread already in * queue. Used by condition wait methods as well as acquire. * * @param node the node * @param arg the acquire argument * @return {@code true} if interrupted while waiting */ final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { // 自旋的死循环操做方式 final Node p = node.predecessor(); // 若是新建结点的前驱结点是头结点 // 若是前驱结点为头结点,那么该结点则是老二,仅次于老大,也但愿尝试去获取一下锁,万一头结点恰巧刚刚释放呢?但愿仍是要有的,万一实现了呢。。。 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC // 拿到锁资源后,则该node结点升级作头结点,且设置后继结点指针为空,便于GC回收 failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && // 根据前驱结点看看是否须要休息一下子 parkAndCheckInterrupt()) // 阻塞操做,正常状况下,获取不到锁,代码就在该方法中止了,直到被唤醒 interrupted = true; // 若是执行到这里,说明尝试休息失败了,由于是自旋操做,因此还会再次循环继续操做判断 } } finally { if (failed) cancelAcquire(node); } } 二、acquireQueued也是采用一个自旋的死循环操做方式,只有头结点才能尝试获取锁资源,其他的结点挨个挨个在那里等待修改,等待被唤醒,等待机会成为头结点; 而新添加的node结点也天然逃不过如此命运,先看看是否头结点,而后再看看是否能休息;
一、源码: /** * Checks and updates status for a node that failed to acquire. * Returns true if thread should block. This is the main signal * control in all acquire loops. Requires that pred == node.prev. * * @param pred node's predecessor holding status * @param node the node * @return {@code true} if thread should block */ private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; // 获取前驱结点的状态值 if (ws == Node.SIGNAL) // 若前驱结点的状态为SIGNAL状态的话,那么该结点就不要想事了,直接返回true准备休息 /* * This node has already set status asking a release * to signal it, so it can safely park. */ return true; if (ws > 0) { /* * Predecessor was cancelled. Skip over predecessors and * indicate retry. */ // 若前驱结点的状态为CANCELLED状态的话,那么就一直向前遍历,直到找到一个不为CANCELLED状态的结点 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ // 剩下的结点状态,则设置其为SIGNAL状态,而后返回false标志等外层循环再次判断 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } 二、shouldParkAfterFailedAcquire主要是检测前驱结点状态,前驱结点为SIGNAL的话,则新结点能够安安心心休息了; 若是前驱结点大于零,说明前驱结点处于CANCELLED状态,那么则以入参pred前驱为起点,一直往前找,直到找到最近一个正常等待状态的结点; 若是前驱结点小于零,那么就将前驱结点设置为SIGNAL状态,而后返回false依赖acquireQueued的自旋再次判断是否须要进行休息;
一、源码: /** * Convenience method to park and then check if interrupted * * @return {@code true} if interrupted */ private final boolean parkAndCheckInterrupt() { LockSupport.park(this); // 阻塞等待 return Thread.interrupted(); // 被唤醒后查看是否有被中断过否? } 二、parkAndCheckInterrupt首先调用park让线程进入等待状态,而后当park阻塞被唤醒后,再次检测是否曾经被中断过; 而被唤醒有两种状况,一个是利用unpark唤醒,一个是利用interrupt唤醒;
一、源码: public void unlock() { sync.release(1); // } 二、unlock释放锁资源,通常都是在finally中被调用,防止当临界区由于任何异常时怕锁不被释放; 而释放锁不像获取锁lock的实现多色多样,没有所谓公平或不公平,就是规规矩矩的释放资源而已;
一、源码: /** * Releases in exclusive mode. Implemented by unblocking one or * more threads if {@link #tryRelease} returns true. * This method can be used to implement method {@link Lock#unlock}. * * @param arg the release argument. This value is conveyed to * {@link #tryRelease} but is otherwise uninterpreted and * can represent anything you like. * @return the value returned from {@link #tryRelease} */ public final boolean release(int arg) { if (tryRelease(arg)) { // 尝试释放锁资源,此方法由AQS的具体子类实现 Node h = head; if (h != null && h.waitStatus != 0) // 从头结点开始,唤醒后继结点 unparkSuccessor(h); // 踢出CANCELLED状态结点,而后唤醒后继结点 return true; } return false; } 二、release尝试释放锁,而且有义务移除CANCELLED状态的结点,还有义务唤醒后继结点继续运行获取锁资源;
一、源码: // NonfairSync 和 FairSync 的父类 Sync 类的 tryRelease 方法 protected final boolean tryRelease(int releases) { int c = getState() - releases; // 获取锁资源值并作减1操做 if (Thread.currentThread() != getExclusiveOwnerThread()) // 查看当前线程是否和持有锁的线程是否是同一个线程 // 正常状况下,须要释放的线程确定是持有锁的线程,不然不就乱套了,确定哪里出问题了,因此抛出异常 throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { // 若此时锁资源值作减法操做后正好是0,则全部锁资源已经释放干净,所以持有锁的变量也置为空 free = true; setExclusiveOwnerThread(null); } setState(c); // 若此时作减法操做尚未归零,那么这种状况就是那种重入锁,须要重重释放后才行 return free; } 二、tryRelease主要经过CAS操做对state锁资源进行减1操做;
一、源码: /** * Wakes up node's successor, if one exists. * * @param node the node */ private void unparkSuccessor(Node node) { /* * If status is negative (i.e., possibly needing signal) try * to clear in anticipation of signalling. It is OK if this * fails or if status is changed by waiting thread. */ // 该node通常都是传入head进来,也就是说,须要释放头结点,也就是当前结点须要释放锁操做,顺便唤醒后继结点 int ws = node.waitStatus; if (ws < 0) // 若结点状态值小于0,则归零处理,经过CAS归零,容许失败,可是无论怎么着,仍然要往下走去唤醒后继结点 compareAndSetWaitStatus(node, ws, 0); /* * Thread to unpark is held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor. */ Node s = node.next; // 取出后继结点,这个时候通常都是Head后面的一个结点,因此通常都是老二 if (s == null || s.waitStatus > 0) { // 若后继结点为空或者后继结点已经处于CANCELLED状态的话 s = null; // 那么从队尾向前遍历,直到找到一个小于等于0的结点 // 这里为何要从队尾向前寻找? // * 由于在这个队列中,任何一个结点都有可能被中断,只是有可能,并不表明绝对的,但有一点是肯定的, // * 被中断的结点会将结点的状态设置为CANCELLED状态,标识这个结点在未来的某个时刻会被踢出; // * 踢出队列的规则很简单,就是该结点的前驱结点不会指向它,而是会指向它的后面的一个非CANCELLED状态的结点; // * 而这个将被踢出的结点,它的next指针将会指向它本身; // * 因此设想一下,若是咱们从head日后找,一旦发现这么一个处于CANCELLED状态的结点,那么for循环岂不是就是死循环了; // * 可是全部的这些结点当中,它们的prev前驱结点仍是没有被谁动过,因此从tail结点向前遍历最稳妥 for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) s = t; } if (s != null) LockSupport.unpark(s.thread); // 唤醒线程 } 二、unparkSuccessor主要是踢出CANCELLED状态结点,而后唤醒后继结点; 可是这个唤醒的后继结点为空的话,那么则从队尾一直向前循环查找小于等于零状态的结点并调用unpark唤醒;
Java多线程进阶(十)—— J.U.C之locks框架:基于AQS的读写锁(5)