highlight: arduino-light
await源码
``` 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() { }
public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int 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; if (t != null && t.waitStatus != Node.CONDITION) { unlinkCancelledWaiters(); t = lastWaiter; } Node node = new Node(Thread.currentThread(), Node.CONDITION); if (t == null) firstWaiter = node; else //只有1个指针nextWaiter //说明是1个单向队列 t.nextWaiter = node; lastWaiter = node; return node; } } ```
public class await { public static void main(String[] args) { ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); for (int i = 0; i < 10; i++) { Thread thread = new Thread(() -> { lock.lock(); try { //10个线程依次获取锁,然后执行await方法 //此时这10个线程对应10个node放在队列里 condition.await(); } catch (InterruptedException e) { e.printStackTrace(); }finally { lock.unlock(); } }); thread.start(); } } }
线程等待队列是双向链表即同步队列是双向链表,Codition等待队列是单链表
await
package test; import java.util.concurrent.locks.ReentrantLock; public class ConditionObject implements Condition, java.io.Serializable { private static final long serialVersionUID = 1173984872572414699L; // 第一个等待节点 初始都是null private transient Node firstWaiter; // 最后一个等待节点 初始都是null private transient Node lastWaiter; public ConditionObject() { } //await方法 public final void await() throws InterruptedException { //如果调用await的线程已经是中断状态抛出异常 if (Thread.interrupted()){ throw new InterruptedException(); } //添加节点入Condition等待队列 //进入addConditionWaiter Node node = addConditionWaiter(); // 释放当前线程所有的重入锁 并唤醒后继节点 // 因为当前线程的锁释放后肯定要唤醒其他等待的节点去获取锁 // 这说明了调用await方法必须是已经获得了condition引用(关联)的lock。 int savedState = fullyRelease(node); //默认的打断模式是 0 即没有被打断 int interruptMode = 0; /*** isOnSyncQueue(node)判断是否在同步队列 此时是不在同步队列:所以返回false !isOnSyncQueue(node)=true 然后进入 LockSupport.park(this);阻塞 **/ while (!isOnSyncQueue(node)) { //将当前线程park LockSupport.park(this); //思考下:什么情况下会从park的状态解除? //1.当前线程被interrupt //2.当前线程被signal即LockSupport.unpark //当从park的状态解除以后,会调用checkInterruptWhileWaiting()方法 //判断自己在park过程中有没有被中断过。 //返回0表示没有被中断过 //返回THROW_IE = -1 表示需要抛出异常,因为只有在signal之前才能cas将0变成-2,进入同步队列成功。 //返回1表示需要重置中断标识 /*** 思考:什么时候会在同步队列?即什么时候跳出循环? 1.当前节点被移动到同步队列,即其他线程调用condition的signal或者signalAll,并把当前节点加入同步队列并修改状态为0 2.在signal方法之前或者之后被interrupt,interruptMode!=0主动执行break;跳出循环 如果是signal方法之前被interrupt 返回-1表示需要抛出异常 如果是signal方法之后被interrupt 返回1表示需要重置中断标识 因为只有在signal之前的interrupt才能cas将0变成-2,进入同步队列成功。 因为只有在signal之前的interrupt才能cas将0变成-2,进入同步队列成功。 因为只有在signal之前的interrupt才能cas将0变成-2,进入同步队列成功。 为什么? 因为如果先执行signal,必然是由执行signal方法的线程把当前节点加入同步队列并修改状态为0 否则cas是成功不了的 当退出while循环后就会调用acquireQueued(node, savedState) acquireQueued的作用是在自旋过程中不断尝试获取同步状态,直至成功获取到lock。 ***/ if ((interruptMode = checkInterruptWhileWaiting(node)) != 0){ break; } } // 打断模式 - 在退出等待时重新设置打断状态 // private static final int REINTERRUPT = 1; // 打断模式 - 在退出等待时抛出异常 //private static final int THROW_IE = -1; //前提:(线程没有被打断并且被调用了signal方法被唤醒) 或者 (是在signal之后被打断) //那么就尝试去获取锁 //如果获取不到就继续在同步队列park阻塞直到获取到锁 //如果获取到锁,并且打断是在signal之后 //interruptMode = REINTERRUPT; //这里无视打断模式也是不可打断的体现 if (acquireQueued(node, savedState) && interruptMode != THROW_IE){ interruptMode = REINTERRUPT; } //当在同步队列中获取到锁继续往下走 如果当前节点的下一个节点 不为空 帮忙清理在等待队列被取消的节点 // 清除等待队列中被取消的线程 if (node.nextWaiter != null){ unlinkCancelledWaiters(); } if (interruptMode != 0){ //说明被打断 reportInterruptAfterWait(interruptMode); } }
addConditionWaiter:进入等待队列
// ㈠ 添加一个 Node 至等待队列 private Node addConditionWaiter() { //获取最后一个节点 Node t = lastWaiter; //Node.CONDITION=-2 表示结点等待在Condition上 //不等于-2 代表 尾结点没有在condition等待队列上 // if (t != null && t.waitStatus != Node.CONDITION) { //将所有已取消的 Node 从队列链表断开 unlinkCancelledWaiters(); t = lastWaiter; } // 这里很重要 这里很重要 这里很重要 // 创建一个关联当前线程的新Node,并设置状态为-2 // 创建一个关联当前线程的新Node,并设置状态为-2 // 创建一个关联当前线程的新Node,并设置状态为-2 // 后面在判断节点在await的时候是否被interrupt,interrupt是在signal之前打断还是之后打断使用的是cas // 原始值就是Node.CONDITION=-2 Node node = new Node(Thread.currentThread(), Node.CONDITION); //如果尾节点是空 if (t == null) //将新加入的节点给头结点 firstWaiter = node; else //将新加入的节点给尾节点的下一个节点 //添加第一个node的时候n1即是头也是尾 //添加第二个node的时候n1是头n2是尾 相当于将第二个节点n2放入头结点n1的下一个节点 //添加第三个node的时候n3是尾相当于将第3个节点放入原尾结点n2的下一个节点 //所以这里是个单向链表 t.nextWaiter = node; //最后把lastWaiter指向新加入的node lastWaiter = node; return node; }
private void unlinkCancelledWaiters() { //头结点 Node t = firstWaiter; Node trail = null; while (t != null) { //头结点的下一个节点 Node next = t.nextWaiter; //如果头结点的状态不是-2 需要出队 if (t.waitStatus != Node.CONDITION) { t.nextWaiter = null; //将头结点的下一个节点作为新的头结点 if (trail == null) firstWaiter = next; else trail.nextWaiter = next; //如果头结点的下一个节点是空 说明只有头结点 //将trail赋值给lastWaiter if (next == null) lastWaiter = trail; } else{ trail = t; } t = next; } }
fullyRelease:释放锁
// ㈣ 因为某线程可能重入,需要将 state 全部释放 final int fullyRelease(Node node) { //此处的节点是要await的节点 boolean failed = true; try { //注意这里释放的是多次加锁累加的state //注意这里释放的是多次加锁累加的state int savedState = getState(); if (release(savedState)) { failed = false; return savedState; } else { throw new IllegalMonitorStateException(); } } finally { //这里保证了异常也会被移除 if (failed) node.waitStatus = Node.CANCELLED; } } //释放头节点 public final boolean release(int arg) { //注意这里释放的是多次加锁累加的state if (tryRelease(arg)) { Node h = head; //头结点的waitStatus可能是0 //原因是解锁的时候会用cas修改头结点的waitStatus=0 //如果是0为什么不需要唤醒呢? // h == null 无等待队列 // h.waitStatus == 0 说明后面没有阻塞的队列,即不需要唤醒。 if (h != null && h.waitStatus != 0){ //h是头节点 //如果头结点的后置节点为空或被取消 //从队列的末尾从后往前找,找到最前面一个需要unpark的节点 //否则就唤醒头节点的下一个节点 unparkSuccessor(h); } //释放成功返回true return true; } return false; } //尝试释放 protected final boolean tryRelease(int releases) { int c = getState() - releases; if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) { free = true; setExclusiveOwnerThread(null); } setState(c); return free; }
unparkSuccessor:唤醒同步队列上的节点
private void unparkSuccessor(Node node) { //此处的node节点为头结点 // 如果头节点状态为 小于0 尝试重置状态为 0 // 尝试将node的等待状态置为0,这样的话,后继争用线程可以有机会再尝试获取一次锁。 int ws = node.waitStatus; if (ws < 0) { compareAndSetWaitStatus(node, ws, 0); } //获取头结点的下一个节点 Node s = node.next; //node是头结点 //如果头结点的后置节点为空或被取消 //从队列的末尾从后往前找,找到最前面一个需要unpark的节点 //否则就唤醒头节点的下一个节点 if (s == null || s.waitStatus > 0) { s = null; //循环遍历从 AQS 队列从后至前找到队列中最前面一个需要unpark的节点 //注意这里做了判断t不等于null且t不等于头结点且t.waitStatus <= 0 //也就是找到的节点必定是有效的 for (Node t = tail; t != null && t != node; t = t.prev) //最后一个是有效的 //还会继续往前找倒数第二个是不是有效 if (t.waitStatus <= 0){ s = t; } } //唤醒头结点的下一个节点 去获取锁 if (s != null) //唤醒线程 LockSupport.unpark(s.thread); } }
isOnSyncQueue:判断是否在同步队列
//默认新创建的node就是 Node.CONDITION final boolean isOnSyncQueue(Node node) { //等于-2说明在condition等待队列中 不在同步队列 返回false即可 if (node.waitStatus == Node.CONDITION || node.prev == null) return false; // If has successor(继任者), it must be on sync queue if (node.next != null) return true; //从队列尾部开始找 找到就是 true 否则就是 false return findNodeFromTail(node); } //从队列尾部开始找 找到就是 true 否则就是 false private boolean findNodeFromTail(Node node) { Node t = tail; for (;;) { if (t == node) return true; if (t == null) return false; t = t.prev; } }
checkInterruptWhileWaiting:检查await期间是否中断
唤醒park,判断是否被打断和被打断时机
``` //Thread.interrupted() 返回true 说明中途被打断过 //进入判断 (transferAfterCancelledWait(node) ? THROWIE : REINTERRUPT) //如果 transferAfterCancelledWait 返回true 说明是在signal之前被打断 返回 THROWIE //如果 transferAfterCancelledWait 返回false 说明是在signal之后被打断 返回 REINTERRUPT //Thread.interrupted() 返回false 说明中途没有被打断过 //checkInterruptWhileWaiting返回0
/* 如果线程被打断: 但进入同步队列成功: THROWIE = -1 退出await()方法需要抛出异常,这种模式对应于中断发生在signal之前。 因为只有在signal之前才能cas将0变成-2,进入同步队列成功。 但是进入同步队列失败: REINTERRUPT=1 退出await()方法需要自我打断,这种模式对应于中断发生在signal之后 因为只有在signal之后,cas将0变成-2必然失败,进入同步队列失败。 */ private int checkInterruptWhileWaiting(Node node) { return Thread.interrupted() ? (transferAfterCancelledWait(node) ? THROWIE : REINTERRUPT) : 0; } /* 如果线程被打断: 但进入同步队列成功: THROW_IE = -1 退出await()方法需要抛出异常,这种模式对应于中断发生在signal之前。 因为只有在signal之前才能cas将0变成-2,进入同步队列成功。 但是进入同步队列失败: REINTERRUPT=1 退出await()方法需要自我打断,这种模式对应于中断发生在signal之后 因为只有在signal之后,cas将0变成-2必然失败,进入同步队列失败。 对应的场景是线程A先调用了signal方法,然后当前节点被移到到了同步队列 然后线程B调用interrupt方法对当前节点进行打断唤醒 也就是线程A把节点挪到了同步队列 还没执行unpark 线程B打断了节点唤醒了线程 执行了 checkInterruptWhileWaiting 这个时间点很短 */ final boolean transferAfterCancelledWait(Node node) { //如果节点的状态使用cas由-2改为0修改成功 //那么进入同步队列并返回true 代表进入同步队列成功 if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) { enq(node); return true; }
//如果上面的代码执行失败 返回false while (!isOnSyncQueue(node)) Thread.yield(); return false; } ```
acquireQueued:
尝试获取锁&打断模式非signal前被打断
//前提:(线程没有被打断并且被调用了signal方法被唤醒) 或者 (是在signal之后被打断) //那么就尝试去获取锁 //如果获取不到就继续在同步队列park阻塞直到获取到锁 //如果获取到锁,并且打断模式是在signal之后 //interruptMode = REINTERRUPT; //这里无视打断模式也是不可打断的体现 if (acquireQueued(node, savedState) && interruptMode != THROW_IE){ interruptMode = REINTERRUPT;
unlinkCancelledWaiters
//断开取消节点 private void unlinkCancelledWaiters() { Node t = firstWaiter; Node trail = null; while (t != null) { Node next = t.nextWaiter; //线程已经被取消 static final int CANCELLED = 1; //线程需要去被唤醒 static final int SIGNAL = -1; //线程正在唤醒等待条件 static final int CONDITION = -2; //线程的共享锁应该被无条件传播 static final int PROPAGATE = -3; //不等于-2说明需要从等待队列移除 if (t.waitStatus != Node.CONDITION) { t.nextWaiter = null; if (trail == null) firstWaiter = next; else trail.nextWaiter = next; if (next == null) lastWaiter = trail; }else{ trail = t; } t = next; } }
reportInterruptAfterWait
// ㈤ 应用打断模式 private void reportInterruptAfterWait(int interruptMode) throws InterruptedException { // 打断模式 - 说明进入等待队列失败,需要自我打断 // private static final int REINTERRUPT = 1; // 打断模式 - 说明进入等待队列成功,需要抛出异常 //private static final int THROW_IE = -1; if (interruptMode == THROW_IE) throw new InterruptedException(); else if (interruptMode == REINTERRUPT) selfInterrupt(); }
static void selfInterrupt() { Thread.currentThread().interrupt(); }
awaitNanos(long nanosTimeout)
// 等待 - 直到被唤醒或打断或超时 public final long awaitNanos(long nanosTimeout) throws InterruptedException { if (Thread.interrupted()) { throw new InterruptedException(); } // 添加一个 Node 至等待队列, 见 ㈠ Node node = addConditionWaiter(); // 释放节点持有的锁 int savedState = fullyRelease(node); // 获得最后期限 final long deadline = System.nanoTime() + nanosTimeout; int interruptMode = 0; // 如果该节点还没有转移至 AQS 队列, 阻塞 while (!isOnSyncQueue(node)) { // 已超时, 退出等待队列 if (nanosTimeout <= 0L) { transferAfterCancelledWait(node); break; } // park 阻塞一定时间, spinForTimeoutThreshold 为 1000 ns if (nanosTimeout >= spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); // 如果被打断, 退出等待队列 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0) break; nanosTimeout = deadline - System.nanoTime(); } // 退出等待队列后, 还需要获得 AQS 队列的锁 if (acquireQueued(node, savedState) && interruptMode != THROW_IE) interruptMode = REINTERRUPT; // 所有已取消的 Node 从队列链表删除, 见 ㈡ if (node.nextWaiter != null) unlinkCancelledWaiters(); // 应用打断模式, 见 ㈤ if (interruptMode != 0) reportInterruptAfterWait(interruptMode); return deadline - System.nanoTime(); } // 等待 - 直到被唤醒或打断或超时, 逻辑类似于 awaitNanos public final boolean awaitUntil(Date deadline) throws InterruptedException { // ... } // 等待 - 直到被唤醒或打断或超时, 逻辑类似于 awaitNanos public final boolean await(long time, TimeUnit unit) throws InterruptedException { // ... } // 工具方法 省略 ...
1.进入await队列
2.释放锁
3.唤醒同步队列的头节点的下一个节点。如果头结点的下一个节点不为空且没有取消,unpark该节点。
否则从尾节点开始找,从后往前找最前面一个是-1状态的节点。
signal源码
// 唤醒 - 必须持有锁才能唤醒, 因此 doSignal 内无需考虑加锁 public final void signal() { //判断当前线程是否是持有锁的线程 不是就报错,这也解释了为什么调用signal为什么要持有锁 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); //获取头结点 Node first = firstWaiter; if (first != null){ //唤醒 doSignal(first); } } // 唤醒 - 将没取消的第一个节点转移至AQS同步队列 private void doSignal(Node first) { //first是头结点firstWaiter do { // 如果头结点下一个节点是null 说明目前只有1个节点 // 将尾节点置为null //firstWaiter是first的下一个节点 if ( (firstWaiter = first.nextWaiter) == null) { lastWaiter = null; } // //断开头结点的下一个节点 即 排在第二的节点 first.nextWaiter = null; //将等待队列中的 Node 转移至 AQS 队列, //如果转移至 AQS 队列失败且还有节点则继续向下循环 ㈢ //每次只会从等待队列移动一个节点到同步队列 //因为transferForSignal返回true代表转移成功 //first = firstWaiter 将first的下一个节点赋值给first } while (!transferForSignal(first) &&(first = firstWaiter) != null); } // 外部类方法, 方便阅读, 放在此处 // ㈢ 如果节点状态是取消, 返回 false 表示转移失败, 否则转移成功 final boolean transferForSignal(Node node) { //node是头结点 // 如果状态已经不是 Node.CONDITION=-2, 说明被取消了 // 同时这里 // 和await方法中addConditionWaiter方法设置为Node.CONDITION呼应 // 和checkInterruptWhileWaiting判断打断时机的代码呼应 因为cas -2 改为 0 只能有1个地方成功 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; // 将等待队列头结点加入同步队列尾部 Node p = enq(node); // 上一个节点被取消 或者 上一个节点 设置状态为 Node.SIGNAL=-1 失败 int ws = p.waitStatus; /*** 下面的代码是为了进一步提升性能,针对两种情况: 如果插入node前,AQS内部等待队列的队尾节点就已经被取消,则满足wc > 0 如果插入node后,AQS内部等待队列的队尾节点已经稳定,满足tail.waitStatus == 0 但在执行ws > 0之后!compareAndSetWaitStatus(p, ws, Node.SIGNAL)之前被取消,则CAS也会失败,满足compareAndSetWaitStatus(p, ws, Node.SIGNAL) == false 这两种情况下,提前唤醒node能够在等待锁的同时,预先完成一部分ConditionObject#await()中无需同步的工作。这部分成本不能被轻易忽视,因为条件队列被应用最多的场景是高并发,大量线程累加起来的成本是很可观的。 链接:https://www.jianshu.com/p/a932c184db52 ***/ 节点被取消的原因: This node is cancelled due to timeout or interrupt. Nodes never leave this state. In particular, a thread with cancelled node never again blocks. //如果上一个节点被取消,或者在执行ws > 0之后 //!compareAndSetWaitStatus(p, ws, Node.SIGNAL)之前 //被取消那么就unpark当前节点的上一个节点 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) { // unpark 取消阻塞, 让线程重新同步状态 LockSupport.unpark(node.thread); } return true; } // 全部唤醒 - 等待队列的所有节点转移至 AQS 队列 private void doSignalAll(Node first) { lastWaiter = firstWaiter = null; do { Node next = first.nextWaiter; first.nextWaiter = null; transferForSignal(first); first = next; } while (first != null); } // 全部唤醒 - 必须持有锁才能唤醒, 因此 doSignalAll 内无需考虑加锁 public final void signalAll() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignalAll(first); }
// 不可打断等待 - 直到被唤醒 public final void awaitUninterruptibly() { // 添加一个 Node 至等待队列, 见 ㈠ Node node = addConditionWaiter(); // 释放节点持有的锁, 见 ㈣ int savedState = fullyRelease(node); boolean interrupted = false; // 如果该节点还没有转移至同步队列, 阻塞 while (!isOnSyncQueue(node)) { // park 阻塞 LockSupport.park(this); // 如果被打断, 仅设置打断状态 if (Thread.interrupted()){ interrupted = true; } } // 唤醒后, 尝试竞争锁, 如果失败进入 AQS 队列 if (acquireQueued(node, savedState) || interrupted) selfInterrupt(); } // ㈠ 添加一个 Node 至等待队列 private Node addConditionWaiter() { Node t = lastWaiter; //Node.CONDITION=-2 表示结点等待在Condition上,!= Node.CONDITION代表结点没有在等待队列上 //所有已取消的 Node 从队列链表断开, 见 ㈡ if (t != null && t.waitStatus != Node.CONDITION) { unlinkCancelledWaiters(); t = lastWaiter; } // 创建一个关联当前线程的新Node, //如果头部为空,那么添加至队列头部,头部尾部都指向这个新node //如果头部不为空,那么添加到队列尾部 Node node = new Node(Thread.currentThread(), Node.CONDITION); if (t == null) firstWaiter = node; else t.nextWaiter = node; lastWaiter = node; return node; } // ㈣ 因为某线程可能重入,需要将 state 全部释放 final int fullyRelease(Node node) { //此处的节点是要await的节点 boolean failed = true; try { //注意这里释放的是多次加锁累加的state int savedState = getState(); if (release(savedState)) { failed = false; return savedState; } else { throw new IllegalMonitorStateException(); } } finally { if (failed) node.waitStatus = Node.CANCELLED; } } }