本文将介绍Java中ReentrantLock的实现原理,从源码层面讲解公平锁和非公平锁的加锁、释放锁的流程,以及条件变量的实现。
ReentrantLock 依赖关系如下图所示:
非公平锁实现原理
ReentrantLock 默认采用非公平锁。
// ReentrantLock
public ReentrantLock() {
sync = new NonfairSync();
}
加锁流程
ReentrantLock 的 lock 方法通过同步器的 lock 方法实现。
// ReentrantLock
public void lock() {
sync.lock();
}
同步器的 lock 方法会先调用 initialTryLock() 方法,如果失败,则调用 acquire() 方法。
// Sync extends AbstractQueuedSynchronizer
final void lock() {
if (!initialTryLock())
acquire(1);
}
// AbstractQueuedSynchronizer
public final void acquire(int arg) {
// 尝试获取锁
if (!tryAcquire(arg))
// 获取锁失败则加入的等待队列
// null, 自定义参数, 非共享锁, 不可打断, 无时限, 时限
acquire(null, arg, false, false, false, 0L);
}
NonfairSync 的 initialTryLock 分两步:尝试获取锁、尝试重入。
NonfairSync 的 tryAcquire 方法被调用前,必定会调用 initialTryLock() 方法检查锁是否被当前线程持有,也即,调用 tryAcquire 方法时,锁必定未被当前线程持有。因此,当未有线程持有锁时,tryAcquire 才能尝试获取锁。
// NonfairSync extends Sync
static final class NonfairSync extends Sync {
// 初次尝试获取锁
final boolean initialTryLock() {
Thread current = Thread.currentThread();
// 通过CAS尝试获取锁
if (compareAndSetState(0, 1)) {
// 将锁的持有者设为当前线程
setExclusiveOwnerThread(current);
return true;
}
// 尝试获取锁失败,判断锁的持有者是否为当前线程
else if (getExclusiveOwnerThread() == current) {
// 锁重入
int c = getState() + 1;
// 整数溢出
if (c < 0)
throw new Error("Maximum lock count exceeded");
// 设置锁的状态:state 表示重入次数
setState(c);
return true;
} else
return false;
}
// 非初次尝试获取锁
protected final boolean tryAcquire(int acquires) {
// 未有线程持有锁时,当前线程尝试获取锁
if (getState() == 0 && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
}
AbstractQueuedSynchronizer 的 acquire 方法负责将线程加入等待队列,acquire 方法的主要执行步骤如下:
- 若非第一个等待线程且前驱存在,则检查当前节点前驱是否已被取消(前驱线程被取消)
- 若已被取消,则需要从队尾开始,往前清理已取消的节点,进入下一轮循环。
- 若未被取消,检查当前线程是否已成为第一个线程,若是,则自旋等待,进入下一轮循环
- 若是第一个等待线程或者前驱不存在,则尝试获取锁,获取锁成功则直接返回 1 表示获取锁成功。
- 准备将当前线程加入等待队列
- 3.1 若等待队列未创建,则创建等待队列,进入下一轮循环
- 3.2 否则,若节点未创建,则创建节点,进入下一轮循环
- 3.3 否则,若节点信息未设置,则设置节点信息(包括将节点加入队尾),进入下一轮循环
- 3.4 否则,若为第一个等待线程且自旋次数不为 0,提示 JVM 当前线程正在忙等,进入下一轮循环
- 3.5 否则,若节点状态为 0(默认),则将节点状态设为 1(WAITING)
- 3.6 否则,重置自旋自旋次数,当前线程陷入等待,被唤醒后将等待状态置为 0。
// AbstractQueuedSynchronizer
final int acquire(
// 尝试获取锁的节点
Node node,
// 自定义参数
int arg,
// 是否为共享锁
boolean shared,
// 是否可打断
boolean interruptible,
// 是否带时限
boolean timed,
// 时限
long time
) {
Thread current = Thread.currentThread();
byte spins = 0, postSpins = 0;
boolean interrupted = false, first = false;
Node pred = null;
for (;;) {
// 检查前驱是否被取消
if (
// 非第一个等待线程
!first &&
// 前驱存在
// 除非本方法被ConditionNode.await()方法调用,否则node初始必为null
// 也即,此条件在第一轮循环必为false
(pred = (node == null) ? null : node.prev) != null &&
// 非第一个等待线程
!(first = (head == pred))
) {
if (pred.status < 0) {
// 若前驱被取消,则需要清理队列中取消的线程
// 此举是为确保给当前节点的前驱能唤醒当前节点
cleanQueue();
continue;
} else if (pred.prev == null) {
// 若前驱的前驱为null,说明当前线程是第一个线程
// !first 和 pred.prev == null 之间,head的值被修改,导致当前线程成为第一个线程
// 第一个线程自旋等待,以减少线程切换的开销
Thread.onSpinWait();
continue;
}
}
// 尝试获取锁
if (
// 第一个线程 或者 前驱不存在
first || pred == null
) {
boolean acquired;
// 尝试获取锁
try {
if (shared)
acquired = (tryAcquireShared(arg) >= 0);
else
acquired = tryAcquire(arg);
} catch (Throwable ex) {
cancelAcquire