总结性概述
- signalAll:将节点从条件队列中移除,并尾部插入同步队列中
1.1 unlock时从同步队列中的头部开始解锁 - await: 尾部插入条件队列;释放锁;阻塞;
2.1 await解除阻塞后(unlock或者中断)
(1)获取锁
(2)响应中断
1. 在等待时中断,则抛出中断异常
2. 在已经解除阻塞后中断,则自己中断自己
源码分析测试代码介绍
-
启动3个线程,分别执行存放、取出、中断操作
-
线程2
2.1 首先执行线程2,此时集合breads为空,则执行notEmpty.await();阻塞 -
线程1
3.1 因为线程2执行await已经释放了锁,所以线程1可以通过lock.lock();获取锁
3.2 让集合breads添加元素,通知线程1:notEmpty.signalAll(); -
线程3
4.1 线程3的目的是为了测试在await期间线程中断的场景
Contion#await源码分析
-
线程是否中断,是则抛出中断异常
-
Node node = addConditionWaiter();尾部插入条件队列
2.1 条件队列是个单向链表结构,由nextWaiter属性维护链表结构
2.2 如果尾部节点:lastWaiter的等待状态:waitStatus不为Node.CONDITION,则清除(暂未写测试代码分析,感觉像是从头开始清除不为CONDITION的节点)
2.3 新建节点信息(node),插入条件队列 -
释放锁:int savedState = fullyRelease(node)
3.1 释放锁的逻辑与ReentrantLock#unlock一样(可参考之前的文章) -
判断节点是否在同步队列中:isOnSyncQueue(node)
4.1 如果节点状态为CONDITION或者前置节点为空,则说明不在同步队列中
–> 加入同步队列时,会初始化一个头,然后尾部插入
4.2 同步队列(双向链表)由prev、next维护,那么如果Next不为空,则说明在同步队列中
4.3 从尾部往前开始同步队列,直到找到该节点,则说明在同步队列,没找到则说明不在
4.4 如果不在同步队列中则阻塞:LockSupport.park(this); 先跳到signalAll分析
4.5 检查在等待的过程中,线程是否中断:checkInterruptWhileWaiting(node)
4.5.1 如果没有被中断,则返回0,再一次判断isOnSyncQueue,返回false
4.5.2 如果被中断,则
(1) 在条件等待时被中断,则尾部插入同步队列:enq(node),中断模式为THROW_IE
(2) 在释放锁后被中断,则返回flase,中断模式为REINTERRUPT
1. while循环从注解描述,为了防止一种瞬时情况(没有深究)
(3)跳出循环,直接Break
4.6 从同步队列的头开始获取锁:acquireQueued(node, savedState)
(1)在获取锁失败后阻塞,这时线程被中断才会返回true
4.7 如果条件队列中下一个等待节点不为空,则遍历清除已经取消的等待节点
4.8 响应等待时中断:reportInterruptAfterWait(interruptMode)
(1)在条件等待时被中断,则抛出中断异常
(2)在释放锁后被中断,则中断线程(因为之前通过Thread.interrupted()判断复位了中断位)
Condition#signalAll源码分析
- 判断当前是否持有线程:isHeldExclusively,不是则抛出IllegalMonitorStateException异常
- 从头开始通知:doSignalAll(first)
2.1 将头节点从条件队列里移除,然后执行transferForSignal(first)
(1)CAS操作将节点状态由CONDITION修改为0
(2)尾部插入同步队列:Node p = enq(node);
–>返回的插入节点的前置节点
(3)前置节点的状态>0或者CAS修改状态为阻塞失败,则解除阻塞:LockSupport.unpark(node.thread);
–> 调试时没走解除阻塞逻辑,ws=0
2.2 遍历执行2.1 操作 - unlock释放锁(此时回到await分析的4.5)
3.1 从同步队列中从头开始解除阻塞