JDK8 源码解读:ReentrantLock - 变量与结构
前言
ReentrantLock 的许多具体实现都在 AbstractQueuedSynchronizer 中,因此本应该先阅读 AQS 的源码。
但是我在阅读时就发现,由于 AQS 中同时包含独占锁与包容锁的实现,因此直接阅读很多地方很费解,因此我觉得从某种锁入手同步阅读 AQS 效果会更好。
虽说我个人认为不先进行 AQS 的代码阅读,但是我们还是应该要先阅读里面用到的变量。
PS:Alt + 7 调出 Structure 工具方便对方法变量进行快速定位,本次源码中暂时不涉及 ReentrantLock 中的条件部分
ReentrantLock 与 AQS 的关系
ReentrantLock: 主要提供何时加锁何时解锁的逻辑,至于加锁解锁是设计到的底层数据结构的变化,全部由 AQS 提供
AbstractQueuedSynchronizer: 队列同步器,底层提供了线程加锁,解锁与重入的具体操作实现,是 ReentrantLock 的核心组件
ReentrantLock 变量
sync:同步器
Sync 同步器继承了 AQS,为 ReentrantLock 提供核心实现
Sync 在 ReentrantLock 中根据不同的构造函数主要有两个实现,分别对应公平锁与非公平锁
private final Sync sync;
AQS 变量
head:等待队列头结点
指向等待队列首元素的头指针
延迟初始化,除了初始化,只能通过 setHeader 修改
如果 head 存在,保证 head 节点不会被 Cancelled(线程取消了等待)
head 表示正在进行的节点,因此不满足等待的情况,因此不可能为 Cancelled
private transient volatile Node head;
tail:等待队列尾结点
指向队列尾元素的尾指针
延迟初始化,只通过 enq 方法来新增等待节点
private transient volatile Node tail;
state:同步状态
- 0:未进入同步
- 1:进入同步状态
- >1:说明线程重入了,值为 1 + 重入次数
因此这个字段很重要,重入次数靠它实现
private volatile int state;
用于原子操作的变量
这几个参数就不一一介绍了,官方注释也很清晰,意思就是这几个是用来对类中的某些变量进行原子性 CAS 操作的,调用的是底层 C 语言中的方法
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
}
AQS 等待队列节点 Node
静态参数
涉及模式
- SHARED: 标记表示节点正在共享模式中等待
- EXCLUSIVE: 标记表示节点正在独占模式下等待
/** Marker to indicate a node is waiting in shared mode */
// 标记表示节点正在共享模式中等待
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
// 标记表示节点正在独占模式下等待
static final Node EXCLUSIVE = null;
等待状态
由于暂时不涉及 ReentrantLock 条件部分,因此不涉及条件等待队列部分,等待状态仅涉及
CANCELLED,SIGNAL ,以及INITIALIZE
- CANCELLED: 1,取消状态,表示线程已经无法再运行
- INITIALIZE: 0,初始化状态,虽然代码中并没有明确指出
- SIGNAL : -1,待唤醒状态,一般表示线程未轮到,在队列中等待唤醒
- CONDITION : -2,线程等待在条件变量队列中
- PROPAGATE : -3,在共享模式下,无条件传播releaseShared状态,引入这个状态是为了解决共享锁并发释放引起线程挂起的bug 6801020
/** waitStatus value to indicate thread has cancelled */
// 表示线程取消了等待,比如取得锁的过程中报异常情况下
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
// 表示后续节点需要被唤醒
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
// 线程等待在条件变量队列中。
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
// 在共享模式下,无条件传播releaseShared状态
// 引入这个状态是为了解决共享锁并发释放引起线程挂起的bug 6801020
static final int PROPAGATE = -3;
变量
waitStatus:等待状态
用于接收具体的等待状态,本次只涉及 CANCELLED,SIGNAL 以及初始化状态
volatile int waitStatus;
prev:指向前一个节点
此参数在进入队列时分配,出队列时清空,便于 GC
这个节点必定会被找到,因为 head 节点是无法取消的,因此至少能找到 head 节点
作用1: 用来检查 waitStatus 状态
作用2: 在上一个任务被取消后,会一直循环直到找到上一个没被取消的节点
volatile Node prev;
next:指向后一个结点的指针
此参数在进入队列时分配,出队列时清空,便于 GC
在 enq 时候并未分配这个参数,因此这个参数为 null,并不代表这个节点为尾结点
特殊情况:后一个节点 prev 在 CAS 还未分配完的情况下,前一个节点的 next 此时为空,但是并非尾结点
如果下个节点为 null,需要从尾结点向前扫描以此进行双重验证
假如下个节点被取消了,并非下个节点为 null,而且下个节点的 next 指向为 null
volatile Node next;
thread:当前线程
volatile Thread thread;
nextWaiter:指向下一个等待在条件变量队列中的节点
指向下一个等待在条件变量队列中的节点
因为条件队列仅在独占模式时才被访问(EXCLUSIVE)
因此只需要一个简单链表队列来保存条件变量队列中的节点
将它们放入队列重新获取许可,由于条件队列仅在独占模式下使用,因此使用特殊值来代表共享模式
Node nextWaiter;
结尾
JDK8 源码解读:ReentrantLock - 核心方法