8.await和signal源码


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;       }   } ​ }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值