前言
上篇博客讲解了Sychronized的底层实现原理,它是基于jdk实现的,现在此篇博客讲解基于AQS 框架实现的另一个锁ReentrantLock
1、名词解释
为什么叫CLH队列
CLH 全称是三个人创造的,分别为Craig、Landin、Hagersten,所以以这个三个名字的首字母命名,原始的CLH锁是一种单向链表结构的自旋锁队列
每个节点仅包含:
前驱指针(myPred):指向队列中前一个节点
状态变量(locked):表示当前线程是否需要获取锁(true=需要自旋等待,false=可获取锁)
线程信息:封装当前等待线程
但是在ReentrantLock里它对原来的CLH队列进行了升级,Java 并发包中的 AQS 框架采用 CLH 锁的变体(虚拟双向队列(FIFO队列)),通过封装线程为 Node 对象实现锁分配。
2、ReentrantLock的实现
2.1 Node的数据结构
在AQS框架中,是使用队列的方式来实现同步管理的,队列中每个节点是Node节点,看下Node的数据结构,
static final Node SHARED = new Node();
static final Node EXCLUSIVE = null;
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
源码如上,其实核心的结构就是
thread:一个等待获取同步状态的线程
prev:指向上一个节点的引用
next:指向下一个节点的引用
waitStatus:等待的状态:
1.CANCELLED:值尾1,表示在同步队列中等待的线程等待超时或被中断,需要从同步队列中取消等待,接入CANCELLED状态后将不会再变化
2.SIGNAL:值尾-1,表示后续节点的线程处于等待状态,如果当前节点的线程释放了同步状态或者被取消便会通知后续节点,使后续节点的线程得以运行
3.CONDITION,值为-2 表示节点再条件队列中,节点等待线程再Condition上,当其它线程队Condition调用了signal()后,该节点将会从条件队列中转移到同步队列中
4.PROPAGATE:值为-3,表示下一次共享式同步状态获取将会无条件传播下去nextWaiter:表示条件队列中的后续节点,如果当前节点是共享的,那么这个字段将是一个SHARED变量static final Node SHARED = new Node();,也就是说节点类型(独占和共享)和条件队列中的后续节点共用同一个字段EXCLUSIVE:独占模式节点
AQS实现原理
上边介绍了AQS 中的Node节点,现在说下AQS实现的原理
核心元素
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
// Node节点
static final class Node {
...}
// 指向头节点
private transient volatile Node head;
// 指向尾节点
private transient volatile Node tail;
// 表示资源的可用状态
private volatile int state;
}
AQS 中的同步等待队列结构
AQS中 维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。
这里volatile能够保证多线程下的可见性,当state=1则代表当前对象锁已经被占有,其他线程来加锁时则会失败,加锁失败的线程会被放入一个FIFO的等待队列中,比列会被LockSupport.park()操作挂起,等待其他获取锁的线程释放锁才能够被唤醒。另外state的操作都是通过CAS来保证其并发修改的安全性。锁的可重入性state也会有所体现,当state等于0说明没有其它线程获取锁,可以被获取,获取后会设为1并将exclusiveOwnerThread设置为获取到锁的线程,如果不是等于0则判断是否是exclusiveOwnerThread的线程,是的话则获取锁,state+1,否则进入等待队列进行排队。需要注意的是head 指向的是一个虚拟节点,该节点不关联任何线程(thread=null),仅作为队列的起始标识。这种设计简化了队列操作,避免在无竞争时频繁创建和销毁节点

源码分析
1、ReentrantLock下的lock方法
public ReentrantLock() {
sync = new NonfairSync


最低0.47元/天 解锁文章
1057

被折叠的 条评论
为什么被折叠?



