AQS大体结构图
- 前面讲解的可重入锁和可重入读写锁都是围绕着阻塞队列讲解的。
- 没有提到AQS中的另外一个重要内容:等待队列,也称之为条件(condition)队列。
- AQS有Node对象,其有两个用途:造成等待队列和阻塞队列。
- 虽然是Node,挺像链表的,可是jdk的注释中只用了queues这个单词,因此都称之为队列,毕竟它倾向于FIFO。
以ReentrantLock中的newCondition为例
不厌其烦的阅读jdk源码c++
newCondition()方法
关于AQS中的Node的状态
- 状态字段,仅取值: SIGNAL:此节点的后继节点被(或即将)阻塞(经过park),所以当前节点在释放或取消时必须解除其后继节点的park。 为了不竞争,获取方法必须首先代表它们须要一个信号(signal),而后重试原子获取,而后在失败时阻塞。 CANCELLED:因为超时或中断,该节点被取消。 节点永远不会离开这个状态。 特别是,取消节点的线程永远不会再次阻塞。 CONDITION:该节点当前在条件队列中。 它在传输以前不会用做同步队列节点,此时状态将设置为 0。(此处使用此值与该字段的其余用途无关,但简化了机制。) PROPAGATE:A releaseShared 应该传播到其余节点。 这在 doReleaseShared 中设置(仅适用于头节点)以确保传播继续,即便其余操做已经介入。 0:以上都不是 为了简化使用,数值按数字排列。 非负值意味着节点不须要发出信号,通知后继节点。 所以,大多数代码不须要检查特定值,只需检查符号。 对于普通同步节点,该字段被初始化为 0,对于条件节点,该字段被初始化为 CONDITION。 它使用 CAS 修改(或在可能的状况下,无条件 volatile 写入)。
await方法的实现
其实也没作什么,就建立了等待队列和判断在等待中被中断。程序员
signal方法的实现
小结
关于AQs与synchronized关键字之间的关系:
- synchronized关键字在底层的c++实现中,存在两个重要的数据结构 (集合)︰waitSet,EntryList。
- waitSet中存放的是调用了object的wait方法的线程对象(被封装成了C++的Node对象)。
- EntryList中存放的是陷入到阻塞状态、须要获取monitor的那些线程对象。
- 当一个线程被notify后,它就会从waitset中移动到EntryList中。
- 进入到EntryList后,该线程依然须要与其余线程争抢monitor对象。
- 若是争抢到,就表示该线程获取到了对象的锁,它就能够以排他方式执行对应的同步代码。
- AQS中存在两种队列,分别是Condition对象上的条件队列,以及AQS自己的阻塞队列。
- 这两个队列中的每个对象都是Node实例(里面封装了线程对象),还有节点的状态。
- 当位于Condition条件队列中的线程被其余线程signal后,该线程就会从条件队列中移动到AQS的阻塞队列中。
- 位于AQS阻塞队列中的Node对象本质上都是由一个双向链表来构成的。
- 在获取AQS锁时,这些进入到阻塞队列中的线程会按照在队列中的排序前后尝试获取。
- 当AQS阻塞队列中的线程获取到锁后,就表示该线程已经能够正常执行了。
- 陷入到阻塞状态的线程,依然须要进入到操做系统的内核态,进入阻塞(park方法实现)。
waitSet对应AQS等待队列,且后者能够多个,且互不干扰。
EntryList对应AQS的阻塞队列。面试
最后
最近我整理了整套《JAVA核心知识点总结》,说实话 ,做为一名Java程序员,不论你需不须要面试都应该好好看下这份资料。拿到手老是不亏的~个人很多粉丝也所以拿到腾讯字节快手等公司的Offer数据结构
进【Java进阶之路群】,找管理员获取哦-!ide

