AQS,全称AbstractQueuedSynchronizer
,是Concurrent包锁的核心,没有AQS就没有Java的Concurrent包。它究竟是个什么,咱们来看看源码的第一段注解是怎么说明
看完第一段,总结下java
基于以上概念,咱们看看源码究竟是这么实现这些功能的框架
private volatile int state;
该变量标记为volatile
,说明该变量是对全部线程可见的。做用在于每一个线程改变该值,都会立刻让其余线程可见,在CAS(可见锁概念与锁优化)的时候是必不可少的。在AQS类中,不会直接操做这个值,而是交由它的子类去操做和定义他的做用。函数
AQS中有一个静态内部类Node
,其实现是一个双向链表。head
与tail
则是这个链表的头尾指针。做用是存储获取锁失败的阻塞线程。一样的,这个链表是会被多个线程操做的,因此它里面的变量可能是被标记为volatile
,而且操做也要经过CAS等原子方法去执行。
Node还有一个模式的属性:独占模式和共享模式。独占模式下,锁是线程独占的,而共享模式下,锁是能够被多个线程占用的。工具
对于大多数须要操做的原子属性,都对应会有一个大写的值,它的类是VarHandler
。例如state、head、tail
都有对应的VarHandler,STATE、HEAD、TAIL
。VarHandler是1.9
的新特性,提供了相似于原子操做以及Unsafe操做的功能,里面的原子操做大可能是native方法,比较难查看源码。学习
条件队列,是AQS中一个很是关键内部类。这个名字起很是奇异,让人搞不懂,看它类注释也看不懂说了什么。看看AQS头部注解
这个类是为了让子类支持独占模式的。深刻看其中的源码实现,其实就是Node在功能性上的封装,最终让子类实现让当前线程怎么独占一个Object锁。await()、dosign()
等方法就是让线程阻塞、加入队列、唤醒线程等。AQS框架下基本各类独占的加锁,解锁等操做到最后都是基于这个类实现的。该类是提供给子类去使用的,具体实现等下次说ReentranLock
再深刻了解。有人可能以为为何实现这个内部类,又不用,而是给子类去用,那为何不放到子类去呢?其实答案,很简单,抽象加模板模式。
p.s. 只有独占锁才能配合该类使用。优化
AQS的公用的方法,主要是加锁与解锁方法。如下方法只提供了模板,部分实现仍是在子类当中,直接调用会抛出异常。ui
尝试获取锁,失败则进入队列。
先执行tryAcquire()
(子类实现),成功则直接返回,若是是获取锁失败,则执行addWaiter()
,经过CAS在双向链表的尾部添加一个新独占节点。
而后把节点丢到acquireQueued()
中执行。该方法其实就是自旋尝试获取锁或阻塞线程(子类实现决定)。一开始,获取新节点的前驱节点,若是这个节点是head,则证实只有两个节点,此时再次执行tryAcquire()
尝试获取锁,若获取成功,则不须要中断,成功结束。
若是仍是获取失败,则执行shouldParkAfterFailedAcquire()
,根据前驱节点状态(子类设值)判断是否继续自旋(当waitStatus为初始值,重复上一步,直到前面的节点一直在减小到前驱节点为head)或者阻塞线程(当waitStatus标记为SIGNAL)
最后若是acquireQueued()
返回须要阻塞,则执行selfInterrupt()
设置线程为中断
能够看回acquire()
函数的写法,十分的艺术。利用条件判断的短路规则,实如今if()
条件内嵌套判断执行语音。通常人(笔者本人)若是要实现这个功能,会这么写spa
因此下次遇到相似嵌套if条件判断的语句,能够学习下acquire()
的这种短路写法。赞👍线程
检查线程是否被中断并尝试获取锁,失败则进入队列。线程中断会退出队列。
流程基本和acquire()
相同。不一样点就是,acquireInterruptibly()
在自旋获取过程当中若是线程是中断的,那么就会抛出异常退出流程,而且放弃锁。doAcquireInterruptibly()
方法与acquireQueued()
方法很是类似,不一样就是前者在中断状态下,不会再继续获取锁。注意最后有cancelAcquire()
方法的执行。指针
尝试获取锁,失败则进入队列。当超过指定时间或线程中断会退出队列。
在acquireInterruptibly()
基础上,增长多一个时间判断,超过指定时间,则退出,放弃获取锁。
释放当前锁,并唤醒下一个Node。
尝试释放锁
若释放成功,且waitStatus不为0(证实是SIGNAL的),就会执行unparkSuccessor()
,先取消SIGNAL标志,而后找到最近一个须要SIGNAL的节点,而且唤醒它。
以上方法皆为独占模式,对应都有共享模式的方法。最大的不一样其实就是Node的waitStatus值为PROPAGATE。具体流程与独占大致相同,细节留到ReentrantReadWriteLock
再细了解。
回顾下要点
以上即便AQS的大体内容,可能有些部分难以理解,其实很正常,由于AQS提供的是流程模板与工具,没有实质落地的场景,是比较难理解的。等后面介绍ReentranLock
与ReentrantReadWriteLock
的时候,就能够更好更全面的了解总体AQS框架了。
若是以为还不错,请关注公众号:Zack说码