【打怪升级】【juc】关于aqs
aqs,它是抽象队列同步器AbstractQueuedSynchronizer ,是juc包下的核心组件。
-
关于aqs
aqs,是AbstractQueuedSynchronizer 的简称,位于juc包下。在jdk1.5后,提供了针对并发处理的一些工具。
通过aqs,其实它提供了实现锁和线程同步机制的上层抽象能力,在aqs中通过volidate修饰的共享变量status状态、和一个队列模型,(FIFO先进先出)线程等待队列,主要处理在多线程竞争时阻塞。
我们通过源码来分析aqs的能力,默认以8版本为主:
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { private static final long serialVersionUID = 7373984972572414691L; /** * Creates a new {@code AbstractQueuedSynchronizer} instance * with initial synchronization state of zero. */ protected AbstractQueuedSynchronizer() { } static final class Node { /** 共享节点的引用 */ static final Node SHARED = new Node(); /** 独占节点的引用 */ static final Node EXCLUSIVE = null; /** cancelled 取消节点 */ static final int CANCELLED = 1; /** signal 唤醒后续节点 */ static final int SIGNAL = -1; /** condition 等待节点 */ static final int CONDITION = -2; /** * 传播节点 */ static final int PROPAGATE = -3; /** CLH等待队列中的等待状态 */ volatile int waitStatus; //当前node的上一个节点 volatile Node prev; //当前node的下一个节点 volatile Node next; //CLH中每个节点持有的thread volatile Thread thread; //如果是共享节点,就会持有它的引用,并且是SHARED? Node nextWaiter; //是否是共享节点 final boolean isShared() { return nextWaiter == SHARED; } //获取上一个节点,上一个节点可能为空? final Node predecessor() throws NullPointerException { Node p = prev; if (p == null) throw new NullPointerException(); else return p; } Node() { // Used to establish initial head or SHARED marker } //node的构造 Node(Thread thread, Node mode) { // Used by addWaiter this.nextWaiter = mode; this.thread = thread; } Node(Thread thread, int waitStatus) { // Used by Condition this.waitStatus = waitStatus; this.thread = thread; } } //AQS中CLH的中的头节点 private transient volatile Node head; //AQS中CLH的中的尾节点 private transient volatile Node tail; //当前的同步状态 private volatile int state; /** * Returns the current value of synchronization state. * This operation has memory semantics of a {@code volatile} read. * @return current state value */ protected final int getState() { return state; } /** * Sets the value of synchronization state. * This operation has memory semantics of a {@code volatile} write. * @param newState the new state value */ protected final void setState(int newState) { state = newState; } /** * Atomically sets synchronization state to the given updated * value if the current state value equals the expected value. * This operation has memory semantics of a {@code volatile} read * and write. * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that the actual * value was not equal to the expected value. */ protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); } //设置超时时间的锁定,最大时间,超时后进行休眠 static final long spinForTimeoutThreshold = 1000L;
那么,aqs的基本结构应该是这样的:
AQS的实现内部依赖同步队列,可以理解成一个FIFO双向队列,其中队列的元素以Node形式体现,如果线程竞争锁失败,AQS将当前线程通过Node的构建加入到队列中,同时阻塞当前线程,当获取锁的线程释放锁时,会从队列中唤醒下一个符合条件的Node。这种结构的特点是FIFO双向链表,即当前节点包含上一个节点和下一个节点的指针,对于Node来说其实就是包装了Thread的节点信息。每个节点又包含了volidate的state状态量,对节点线程的操作其实也依赖了volidate的状态量。
其中,state包含:
CANCELLED :节点取消运行,这种状态下上一个节点释放后不会唤醒当前节点,而是寻找下一个符合条件的节点,这种类型的节点也会通过补偿机制被移除
SIGNAL :当前节点释放后,唤醒next节点
PROPAGATE :独占锁中,表示当前传播状态
CONDITION:处于等待状态
除此之外,还有不指定state默认为0,代表当前是初始化节点
同时,AQS提供了对state的操作的方法:
protected final int getState() { return state; } /** * Sets the value of synchronization state. * This operation has memory semantics of a {@code volatile} write. * @param newState the new state value */ protected final void setState(int newState) { state = newState; } /** * Atomically sets synchronization state to the given updated * value if the current state value equals the expected value. * This operation has memory semantics of a {@code volatile} read * and write. * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that the actual * value was not equal to the expected value. */ protected final boolean compareAndSetState(int expect, int update) { // See below for intrinsics setup to support this return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
其中,在AQS中修改state的值,大多采用compareAndSetWaitStatus,通过unsafe的native方法。unsafe这里不做过多描述。
-
AQS的操作
AQS的原理,可以理解为,将暂时无法请求到共享资源的线程封装成Node,将Node加入双向队列来实现分配锁。根据volidate,线程根据CAS去改变对应的状态,如果当前请求的资源空闲,则将当前线程操作共享资源并锁定;如果当前已经是锁定状态获取锁失败,就将请求分配到队列中阻塞,等待其他线程完成操作并释放锁,并通知后续节点进行操作。
例如上图中Thread,如果Thread1获取到锁进行操作,那么其余Thread就被封装成Node进行阻塞,等待Thread1操作完成释放锁并唤醒后续节点进行消费。
-
AQS的实现
AbstractQueuedSynchronizer是整个同步机制的基类,如果需要实现同步,一般来说继承AbstractQueuedSynchronizer并重写对应的方法,例如tryAcquire、tryRelease等方法:
tryAcquire(int) | 独占方式。尝试获取资源,成功则返回true,失败则返回false |
tryRelease(int) | 独占方式。尝试释放资源,成功则返回true, 失败则返回false |
tryAcquireShared(int) | 共享方式。尝试获取资源。负数表示失败;大于等于0表示成功,其中0表示没有剩余可用资源 |
tryReleaseShared(int) | tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false |
那对于独占锁和共享锁,又有什么区别呢?
常见的AQS锁有:ReentrantLock
、 Semaphore
、 CountDownLatch
、 CyclicBarrier
、ReentrantReadWritelock
等;
独占锁:表示只有一个线程能操作共享资源,例如ReentrantLock
共享锁:多个线程可以同时操作一个共享资源,例如Semaphore,CountDownLatch,CyclicBarrier
独占+共享:ReentrantReadWritelock,又称为读写锁,读锁是共享锁,写锁是独占锁。
那么公平锁和非公平锁,又怎么理解呢?
公平锁:以队列线程的顺序保证,先入列的线程应该更先获取到锁
非公平锁:没有线程的顺序优先级,靠竞争获取锁
-
AQS源码解析
- 独占锁的获取释放
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
首先 tryAcquire和 tryRelease都是模板方法,需要实现AQS的类去具体实现
针对获取锁的方法,如果tryAcquire获取锁成功了,那么就直接返回了,如果获取失败了,就要加入双端阻塞队列,然后通过 acquireQueued自旋。
private Node addWaiter(Node mode) {
//通过构造包装当前Node 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) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }
首先,addWaiter将当前节点包装成Node,然后将它放在tail的尾部,然后通过自旋入队;如果tail是空的,比如第一个节点,就通过enq的方式入队
private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }
enq的方式,就是通过自旋的方式入队。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true; try {
//是否中断 boolean interrupted = false;
//自旋 for (;;) {
//获取node的上一个节点 final Node p = node.predecessor();
//上一节点必须是头节点并获取锁成功 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; }
//中断 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } }
acquireQueued 也是自旋设置header,首先自旋获取上一个node,如果这个node是头节点才有资格获取独占锁,并等待前一个节点状态为SIGNAL,并且tryAcquire成功,否则将当前线程休眠,等待release唤醒。
如果当前节点处理成功,那么当前的node就会被设置为header,共享锁的获取,需要依赖前一个节点的状态推动。只有当前一个节点处于SIGNAL并且是head的时候当前节点才有机会被处理。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { /** 获取上一个节点的状态,如果是SIGNAL,那么直接返回等待被唤醒*/ int ws = pred.waitStatus; if (ws == Node.SIGNAL) /* * 等待被唤醒 */ return true; if (ws > 0) { /* 如果大于0 那么只能是CANCELLED 那么就要将node之前所有CANCELLED都移除 */ do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { /* 将前一个node状态改为SIGNAL */ compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }
根据上一个节点的状态,处理。如果上一个节点处于SIGNAL,那么就阻塞等待,如果上一个节点已经被取消,那么清除不需要的Node并将当前Node指向上一节点的prev。
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
如果上一节点是SIGNAL,那么让当前节点休眠,等待被唤醒后执行return。
public final boolean release(int arg) { if (tryRelease(arg)) { Node h = head; if (h != null && h.waitStatus != 0) unparkSuccessor(h); return true; } return false; }
释放的流程可以看出,首先通过 tryRelease模板方法,然后根据head节点
只有当head不为空,并且head不是初始状态时才会释放。
private void unparkSuccessor(Node node) { /* 根据head节点状态 */ int ws = node.waitStatus; if (ws < 0) //小于0 可能为SIGNAL, PROPAGATE 那么就直接设置为初始状态 compareAndSetWaitStatus(node, ws, 0); /* 获取head的下一个节点. */ Node s = node.next; //如果下一节点时空或者已经被取消 if (s == null || s.waitStatus > 0) { s = null; //这里是从 tail 尾部开始找的 ? for (Node t = tail; t != null && t != node; t = t.prev) //找到最靠后的状态小于0 的节点 if (t.waitStatus <= 0) s = t; } if (s != null) //唤醒线程 LockSupport.unpark(s.thread); }
释放,就是先通过 release尝试,如果成功并且head可用,就找到符合条件的当前节点的后面一个节点进行唤醒。
例如现在有三个Thread,首先Thread1通过tryAcquire成功,并且获取锁运行,Thread2和3就包装成了Node入队。同时,通过shouldParkAfterFailedAcquire调整状态、修改为SIGNAL;
如果Thread1通过release,这时如果Thread2也通过tryAcquire成功,并且它的上一节点是header,那么Thread2也会被运行,同时Thread2成为了新的header;Thread1的status就会变为0;
如果Thread2也进行release,那么接下来就应该是Thread3 tryAcquire。如果Thread3被取消了,那么就会从后面选择一个新的Node并清除取消的节点。
-
- 共享锁的获取释放
public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) doAcquireShared(arg); }
根据 tryAcquireShared子类对应的实现,这里返回的应该是可用的资源数。实际获取在 doAcquireShared中。
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { //包装成SHARED节点的Node final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { //根据上一节点处理 final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
首先获取的上一节点是否是头节点,只有前置节点是头节点才能做后续的处理。如果可用资源r > 0,那么才会设置为header并且唤醒,如果r = 0那不会唤醒其他节点。
独占锁这里,只是设置了head,没有唤醒的操作。
共享锁这里,可能会唤醒多个线程,这里取决于可用资源的数量。
如果它的前置节点不是head,与独占锁同理,线程休眠。
private void setHeadAndPropagate(Node node, int propagate) { Node h = head; // Record old head for check below //设置node为head setHead(node); //如果可用资源大于0 或者头节点状态是SIGNAL,PROPAGATE 或者头节点是初始化的 if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) { Node s = node.next; //如果是共享节点 或者 当前节点是最后一个节点了 那就释放 if (s == null || s.isShared()) doReleaseShared(); } }
那么,如果说可用资源不够了,< 0 , 那么就需要等待,先将Node添加到对应的队列中,当自己被唤醒的时候,再去唤醒后面的节点进行竞争,一直到没有可以再分配的资源,循环往复。
这时,如果Thread1先获取到资源,并且会把当前资源设置为head,并且当前状态会变为SIGNAL。如果这时还有空闲资源,即propagate>0,Thread1会尝试唤醒Thread2,Thread2被正常唤醒后,则尝试唤醒Thread3。
共享锁释放
private void doReleaseShared() { for (;;) { //找到对应的头节点 Node h = head; if (h != null && h != tail) { int ws = h.waitStatus; if (ws == Node.SIGNAL) { //如果是SIGNAL 说明可以唤醒后续节点 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; // loop to recheck cases //唤醒后续节点 unparkSuccessor(h); } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; } }
共享锁释放,主要考虑的是释放唤醒其他线程竞争设置head节点,会释放最新的head后继节点。
-
- 取消竞争
不管哪种锁,都会在自旋竞争中失败,对于处理失败的节点,需要取消竞争。
private void cancelAcquire(Node node) { // 如果当前节点已经不存在了,那就没有处理它的意义了 if (node == null) return; node.thread = null; // 将已经取消的节点移除, state>0说明是一个被取消的节点 那么让当前节点直接连接到上一个可用的节点 Node pred = node.prev; while (pred.waitStatus > 0) node.prev = pred = pred.prev; // predNext is the apparent node to unsplice. CASes below will // fail if not, in which case, we lost race vs another cancel // or signal, so no further action is necessary. Node predNext = pred.next; //将当前状态设置为取消 node.waitStatus = Node.CANCELLED; //如果当前节点已经是tail了 那么说明它就是最后的一个节点了,那么就将它的上一个节点设置为最后一个tail if (node == tail && compareAndSetTail(node, pred)) { compareAndSetNext(pred, predNext, null); } else { // If successor needs signal, try to set pred's next-link // so it will get one. Otherwise wake it up to propagate. int ws; //如果前置节点不是头节点,并且前置状态为SIGNAL或者可以修改为SIGNAL, 就移除node,并设置前置节点状态为SIGNAL if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) compareAndSetNext(pred, predNext, next); } else { //唤醒node的下一个可用的节点 unparkSuccessor(node); } node.next = node; // help GC } }
其实它的处理流程是:
取消当前节点的状态;
将当前取消的节点的前后符合条件的节点连接起来;
如果前置节点释放锁,那么同时唤醒后续节点;
-
- 响应中断
独占锁中断
public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); }
private void doAcquireInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
可以看到,中断的逻辑主要是处理了中断异常,其中共享锁的处理方式也类似
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg); }
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
-
- 锁超时处理
acquire在处理竞争时,会一直阻塞等待,AQS提供了获取锁超时的机制:超过最大时间后直接失败,不参与竞争。
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { //处理中断状态 if (Thread.interrupted()) throw new InterruptedException(); //竞争下处理 return tryAcquireShared(arg) >= 0 || doAcquireSharedNanos(arg, nanosTimeout); } public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); }
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { //如果没有设置超时时间,放弃获取锁 if (nanosTimeout <= 0L) return false; //计算超时时间 final long deadline = System.nanoTime() + nanosTimeout; //同时将node入队 final Node node = addWaiter(Node.EXCLUSIVE); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return true; } //计算时间是否超时,如果超过了就放弃 nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) return false; //当前程应该被休眠并且时间应该大于指定的时间时,设置时间的park if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } } private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { if (nanosTimeout <= 0L) return false; final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return true; } } nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) return false; if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
-
等待队列
ConditionObject作为AQS下的一个内部类,通过Node的格式定义了一个单向队列,并且它实现了Condition, 实现了线程通信的wait和signal
public class ConditionObject implements Condition, java.io.Serializable { private static final long serialVersionUID = 1173984872572414699L; /** First node of condition queue. */ private transient Node firstWaiter; /** Last node of condition queue. */ private transient Node lastWaiter; //等待队列是一个单向队列,内部维护了队列的首节点和尾节点 // /** * Creates a new {@code ConditionObject} instance. */ public ConditionObject() { } }
其中,每个节点还是对应的Node节点,只是根据Node中的nextWaiter进行连接、每个Node上还是有一个waitStatus。在 ConditionObject中的state的范围:
CONDITION :等待中
CANCELLED :取消
SIGNAL :加入阻塞队列
/** Mode meaning to reinterrupt on exit from wait */ private static final int REINTERRUPT = 1; /** Mode meaning to throw InterruptedException on exit from wait */ private static final int THROW_IE = -1;
REINTERRUPT :返回不抛出异常
THROW_IE:抛出中断异常
public final void await() throws InterruptedException { //中断线程 if (Thread.interrupted()) throw new InterruptedException(); //添加到Condition Node node = addConditionWaiter(); //释放资源 long savedState = fullyRelease(node); int interruptMode = 0; //节点不在阻塞队列 while (!isOnSyncQueue(node)) { LockSupport.park(this); //节点加入同步队列后退出循环 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; } if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; if (node.nextWaiter != null) // clean up if cancelled unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); } //首先加入节点 private Node addConditionWaiter() { Node t = lastWaiter; // 如果最后一个节点不是空且不是CONDITION状态 if (t != null && t.waitStatus != Node.CONDITION) { //取消链接 unlinkCancelledWaiters(); t = lastWaiter; } //创建一个新的节点 Node node = new Node(Thread.currentThread(), Node.CONDITION); if (t == null) //t为空说明目前还没有last,将node设置为头?初始化?否则就放在队尾去 firstWaiter = node; else t.nextWaiter = node; //队尾指向node lastWaiter = node; return node; } final long fullyRelease(Node node) { boolean failed = true; try { long savedState = getState(); //这里认为它就处于 head节点 就进行释放 if (release(savedState)) { failed = false; return savedState; } else { throw new IllegalMonitorStateException(); } } finally { //如果释放失败 那么将节点状态设置为CANCELLED if (failed) node.waitStatus = Node.CANCELLED; } } //是否是在同步队列中 final boolean isOnSyncQueue(Node node) { if (node.waitStatus == Node.CONDITION || node.prev == null) return false; if (node.next != null) // If has successor, it must be on queue return true; /* * node.prev can be non-null, but not yet on queue because * the CAS to place it on queue can fail. So we have to * traverse from tail to make sure it actually made it. It * will always be near the tail in calls to this method, and * unless the CAS failed (which is unlikely), it will be * there, so we hardly ever traverse much. */ return findNodeFromTail(node); } //依次查找 找到了就返回 private boolean findNodeFromTail(Node node) { Node t = tail; for (;;) { if (t == node) return true; if (t == null) return false; t = t.prev; } } private int checkInterruptWhileWaiting(Node node) { return Thread.interrupted() ? (transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) : 0; } private void reportInterruptAfterWait(int interruptMode) throws InterruptedException { if (interruptMode == THROW_IE) throw new InterruptedException(); else if (interruptMode == REINTERRUPT) selfInterrupt(); } final boolean transferAfterCancelledWait(Node node) { if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { enq(node); return true; } /* * If we lost out to a signal(), then we can't proceed * until it finishes its enq(). Cancelling during an * incomplete transfer is both rare and transient, so just * spin. */ while (!isOnSyncQueue(node)) Thread.yield(); return false; }
针对 await其实是在多个Thread调用后创建 addConditionWaiter加入队列,同时释放持有的资源,即先放弃资源后再进行等待。如何同步到AQS?如何唤醒?
再await的while中,如果它不在同步队列中,就要阻塞直到它被唤醒、再加入同步队列
唤醒
public final void signal() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); //获取第一个Node Node first = firstWaiter; if (first != null) //唤醒 doSignal(first); } private void doSignal(Node first) { do { //first之后还有节点吗 如果没有了就把last设置为空 if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; //取消first的指针 first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null); } // final boolean transferForSignal(Node node) { /* * If cannot change waitStatus, the node has been cancelled. */ //先将节点从CONDITION状态改变为0 如果失败可能是节点已经被取消 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; //自旋加入阻塞队列 Node p = enq(node); int ws = p.waitStatus; //如果上一个节点已经取消或者被唤醒了 那就尝试把当前节点也唤醒 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; }
这里唤醒其实就是把Condition的最近的一个休眠的节点加入阻塞队列,并且唤醒竞争
public final void signalAll() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignalAll(first); } private void doSignalAll(Node first) { lastWaiter = firstWaiter = null; //这里跟单独的节点不一样的是 这里会依次找到所有的节点 从first一直找到last do { Node next = first.nextWaiter; first.nextWaiter = null; transferForSignal(first); first = next; } while (first != null); }
本文来自博客园,作者:青柠_fisher,转载请注明原文链接:https://www.cnblogs.com/oldEleven/p/17279652.html