背景
JDK1.5引入的并发包提供了一系列支持中等并发的类,这些组件是一系列的同步器,几乎任一同步器都可以实现其他形式的同步器,例如,可以用可重入锁实现信号量或者用信号量实现可重入锁。但是,这样做带来的复杂性,开销,不灵活使其至多只能是个二流工程,且缺乏吸引力。如果任何这样的构造方式不能在本质上比其他形式更简洁,那么开发者就不应该随意地选择其中的某个来构建另一个同步器,所以JSR166建立了一个小框架-AQS(由Doug Lea设计),对这些同步器做了统一的抽象,为构造同步器提供了通用的机制,之后并发包中大部分同步器都基于AQS来实现。
注:本文通过ReentrantLock来窥探AQS的结构以及运行原理,因为AQS是并发包实现大部分同步器的框架,所以本文只对ReentrantLock相关方法做了解释说明,其他的方法在后面的文章中会继续做深入的解释
AQS设计
这是ReentrantLock中的内部类Sync的类图,图中可以看出Sync抽象类实现了AbstractQueuedSynchronizer。
ReentrantLock实现
ReentrantLock提供了非公平锁以及公平锁的能力,实现Lock接口,通过把功能实现委托给Sync同步器来实现。下面以非公平锁为例子,开始图解ReentrantLock类在调用lock方法时候的过程:
首先看下AQS的数据结构以及Node节点的结构
AQS的数据结构中state是最核心的变量,用来判断当前同步器是否有被线程占用,以及被同一个线程重入了多少次(重入锁实现的关键);
exclusiveOwerThread表示当前是哪个线程占用着同步器;
head是一个指向空的头结点的引用地址;
tail是一个指向等待同步器的最后一个节点的引用地址;
Node节点中最核心的是waitStatus,此处waitStatus的取值分别可以为:
1表示等待的线程已经取消或者中断;
-1表示后一个节点需要唤醒,当前节点如果释放锁,则需要唤醒后继节点;
-2表示当前的节点是一个条件等待,即需要等待其他的条件满足才能够被加入到同步队列,等待被唤醒
-3表示下一个acquireShared应无条件传播(在读写锁中会遇到,后面会专门写文章分析读写锁)
0表示初始状态
看完AQS的数据结构之后,我们再图解ReentrantLock非公平锁的lock方法,看下代码
<