AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源的设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
那么首先看一下CLH队列锁的数据结构及实现算法。
(a)CLH队列的数据结构(如图):
简述:CLH队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配的。具体构建队列的算法是这样的:
假设: 有共享资源S目前正被L3线程占用,此时有L1、L2线程分别对资源S进行lock操作以及获取锁后进行unlock操作。具体的流程如下:
(1)由于目前资源S被占用,所以将线程L1包装成一个CLH队列的Node,将这个Node的前驱(prev)指向当前对列里的队尾,放入队尾这个操作采用了CAS原语(原子操作)。如果当前的队尾为NULL,那么就建一个虚拟的Header,然后将T1线程挂载到虚拟Header下。核心代码如下:
Ps: addWaiter就是放入队列的操作。
Ps:采用CAS将节点加入到队尾,如果队尾为null进入enq操作。
Ps:创建了一个虚拟的Header
(2) L2线程请求资源S,那么它和L1线程一样将自己加入到队尾,L2的prev指向L1,L1.next指向L2(双向队列嘛)。
(3) 当L3释放资源即unlock的时候,唤醒与L3关联的下一个节点,同时释放当前节点。关键代码:
(b)每个结点类的属性及方法信息:
属性简述:CANCELLED:表示因为超时或者中断,结点被设置为取消状态,被取消的状态结点不应该去竞争锁。SIGNAL:表示这个结点的继任结点被阻塞了,因为等待某个条件而被阻塞。CONDITION:表示这个结点在队列中,因为等待某个条件而被阻塞。这几个是常量属性默认值为:
这几个常量用来设置waitStatus属性。
Thread属性表示关联到这个结点的线程。Prev和next就是关联前后结点的索引变量。NextWaiter 记录的是这个结点是独占式还是可共享的属性。
ReentrantLock当中的部分实例代码:
1. 两个构造函数(可见默认使用的非公平锁的分配机制):
2. Lock方法的实现其实就是直接代理了Sync lock的实现:
3. TryLock方法也是一样的,都是代理自Sync
4. 解锁方法