一、原理概述
在AQS中,存在两个FIFO队列:同步队列和等待队列
。等待队列是由Condition内部实现的,是一个虚拟的FIFO单向队列
。
释义: AQS中tali和head主要构成了一个FIFO双向的同步队列,AQS中Condition构成了一个FIFO单向等待队列。Condition是AQS内部类,每个condition对象保存了firstWaiter和lastWaiter作为队列首节点和尾节点,每个节点使用Node.nextWaiter保存下一个节点的引用,因此等待队列是一个单向队列。
二、具体实现
2.1 等待的实现
主要步骤为:
- 当线程调用Condition.await()后,会当前线程封装成Node节点,并将节点加入到等待队列的尾部,然后释放同步state状态(释放锁),唤醒同步队列中的后继节点,当前线程进入等待状态。如下图所示:
注意1:Condition拥有首尾节点的引用,新增节点只需将尾节点的nextWaiter指向它,然后更新尾节点即可。
注意2:Condition.await()节点引用更新的过程并没有CAS保证,原因在于调用await()方法的线程必定是获取
到锁的线程,利用锁来保证线程安全。
注意3:同步队列的首节点并不会直接加入等待队列,而是把当前线程构封装成一个新的节点并将其加入等待队列
中。
2.2 通知的实现
- 执行signal()唤醒线程时,先判断当前线程是否持有state锁,所以能够调用signal()方法的线程一定时持有了同步锁state;
- 【自旋】唤醒等待队列中等待时间最长的节点(firstWaiter首节点),在唤醒firstWaiter节点之前,会将等待队列首节点移动同步队列中。
2.3 总结
- Condition通知的
本质
就是等待队列和同步队列的交互
,和Object的wait()/notify()机制一样。Condition是基于同步锁state实现的,object是基于monitor模式实现的; - 一个lock(AQS)可以有多个等待队列,只有一个同步队列
- Condition.await()方法执行时,会将同步队列里的state锁释放掉,把线程重新封装成node节点添加到等待队列中;Condition.signal()方法执行时,会将等待队列中的首节点移动同步队列尾部,直到state锁被获取才唤醒。