ReentrantLock
本文将详细介绍 JUC 中基于 AQS 独占模式实现的用于并发控制的可重入锁 ReentrantLock 的源码实现
ReentrantLock 是 JUC 中基于 AQS 实现的用于并发控制的可重入锁。
API 简介
ReentrantLock
的核心 API 继承自 java.util.concurrent.locks.Lock
,Lock
接口是 Java 中的锁机制抽象,提供了如下几个方法:
方法 | 说明 |
---|---|
lock() | 获取锁并阻塞当前线程,直到获取到锁为止 |
lockInterruptibly() | 获取锁并阻塞当前线程,直到获取到锁或被中断为止 |
unlock() | 释放锁 |
tryLock() | 尝试获取锁,如果获取成功则返回 true,否则返回 false |
tryLock(long time, TimeUnit unit) | 尝试获取锁,并设置超时时间 |
newCondition() | 返回一个与该锁绑定的条件变量 (Condition),用于线程间的通信和控制 |
除此之外,ReentrantLock
还提供了一些方便开发者使用的特殊方法,包括:
方法 | 说明 |
---|---|
isHeldByCurrentThread() | 判断当前线程是否持有锁 |
getHoldCount() | 返回当前线程持有锁的次数 |
getQueueLength() | 返回等待获取锁的线程数 |
hasQueuedThreads() | 判断是否有线程正在等待获取锁 |
isFair() | 判断是否为公平锁 |
这些特殊方法可以帮助开发者更方便地了解和控制 ReentrantLock
的使用情况。
ReentrantLock Sync 抽象类
ReentrantLock
类中维护了一个继承自 AQS (AbstractQueuedSynchronizer
) 的同步器 Sync
:
abstract static class Sync extends AbstractQueuedSynchronizer {
...
}
Sync 主要方法
lock() 抽象方法
加锁操作分为公平和非公平两种模式,在 Sync
类中,只定义了抽象方法 abstract void lock()
,具体的实现由子类完成。
nonfairTryAcquire()
Sync
同步器提供了一个默认的非公平资源获取方法 nonfairTryAcquire(int)
。源码如下:
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//如果锁未被占用
if (c == 0) {
// 尝试占用锁
if (compareAndSetState(0, acquires)) {
// 绑定当前线程与锁
setExclusiveOwnerThread(current);
return true;
}
}
// 如果锁已被占用,看看是不是当前线程自己持有
else if (current == getExclusiveOwnerThread()) {
//如果是,则重入锁
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
方法 nonfairTryAcquire()
首先会获取 AQS 中的 state
:
- 如果
state == 0
,那么表示锁未被占用,此时方法会直接尝试通过 CAS 获取锁:- 如果抢锁成功,则方法会调用 AQS 父类
AbstractOwnableSynchronizer
中的setExclusiveOwnerThread
方法,将锁与当前线程绑定,并返回 true。 - 如果抢锁不成功,则方法会直接返回 false。
- 如果抢锁成功,则方法会调用 AQS 父类
- 如果
state !=0
,则表示锁已被占用。此时,方法会获取锁绑定的线程:- 如果绑定的线程是当前线程,则说明是重入操作,方法会更新
state
并返回 true。 - 如果绑定的线程不是当前线程,那么说明是其他线程获取的锁,方法会直接返回 false。
- 如果绑定的线程是当前线程,则说明是重入操作,方法会更新
tryRelease()
tryRelease(int)
是 Sync
同步器中提供的释放资源的实现:
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
// 计算释放后剩余的资源数量(即重入次数)
int c = getState() - releases;
// 如果当前执行释放的线程并没有持有锁,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果释放资源后,state 值变成 0,表明锁将被彻底释放
if (c == 0) {
free = true;
// 解绑同步器与线程的关系
setExclusiveOwnerThread(null);
}
// 更新剩余资源值
setState(c);
return free;
}
tryRelease
方法首先会计算释放资源后的剩余资源数量,然后判断当前线程是否持有同步器:
- 如果当前线程未持有同步器,该方法会直接抛出异常。
- 否则,该方法将继续执行以下操作:
- 如果资源释放后剩余资源数量为 0,则表明锁将被彻底释放。此时,该方法会解绑同步器与线程的关系。
最终更新剩余资源数量。
Sync 实现类 NonfairSync
NonfairSync
是 ReentrantLock
中的非公平同步器实现(默认)。源码如下:
static final class NonfairSync extends Sync {
// 加锁操作
@ReservedStackAccess
final void lock() {
// 先通过 CAS 操作,如果失败则调用父类 AQS 中的 acquire
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// 重写 AQS 钩子函数
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
该类实现了两个方法:
- 第一个方法是父类
Sync
中定义的抽象方法实现,即lock()
方法。该方法将 AQS 中抽象资源的概念具象化为锁。如果当前资源数量为 0,则表明锁未被占用,进而通过 CAS 抢锁。如果抢锁失败或者锁已被占用,则委托给 AQS 中独占模式下获取资源的顶层入口 acquire() 方法处理。 - 另一个是在 AQS 独占模式下重写的钩子函数
tryAcquire()
。该方法直接调用了父类Sync
中的 nonfairtryacquire() 方法。
Sync 实现类 FairSync
FairSync
是 ReentrantLock
中的公平同步器实现,它内部也实现了 lock()
、tryAcquire()
两个方法,源码如下:
static final class FairSync extends Sync {
// 加锁操作
final void lock() {
acquire(1);
}
// 重写 AQS 钩子函数
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 如果当前线程前驱节点是头节点,或队列为空,才尝试抢锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
与非公平模式的 lock()
不同,公平模式下的 lock()
直接调用了 AQS 中的 acquire()
方法,实现如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire()
,因此最终仍然回到了公平同步器的实现中。在公平模式下,tryAcquire()
方法在尝试抢锁之前,会先判断当前节点的前驱节点是否为等待队列的头节点或者等待队列是否为空。只有等待队列为空,或者当前节点的前驱节点是等待队列的头节点时,该方法才会去尝试抢锁,否则直接返回 false。除此之外,该方法与非公平模式下的逻辑相同。
ReentrantLock 方法
构造方法
ReentrantLock
提供了两个构造方法:
// 创建一个非公平可重入锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 通过 fair 参数控制是否公平
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
与锁相关的方法
// 加锁操作,通过调用上文的 lock 方法实现
public void lock() {
sync.lock();
}
// 加锁的同时响应中断,调用 AQS 中的方法实现
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
// 尝试加锁,调用 Sync 中的非公平方法实现
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
// 带超时的加锁操作,调用 AQS 中的方法实现
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// 解锁操作,调用 AQS 中的 release 方法释放一个资源
public void unlock() {
sync.release(1);
}
其他方法
ReentrantLock
中的其余方法基本都是通过调用 AQS 及实现类中的方法实现的。实现比较简单,这里直接列出源码:
// 调用 Sync 中的方法,获取 Condition
public Condition newCondition() {
return sync.newCondition();
}
// 判断重入次数,调用上文 Sync 中的方法实现
public int getHoldCount() {
return sync.getHoldCount();
}
// 判断当前线程是否持有锁,调用上文 Sync 中的方法实现
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
// 判断是否有线程持有锁,调用上文 Sync 中的方法实现
public boolean isLocked() {
return sync.isLocked();
}
// 判断是否是公平锁
public final boolean isFair() {
return sync instanceof FairSync;
}
// 获取持有锁的线程
protected Thread getOwner() {
return sync.getOwner();
}
/**
* 判断等待队列是否为空,通过调用 AQS 中的方法实现
* public final boolean hasQueuedThreads() {
* return head != tail;
* }
*/
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
// 判断线程是否在等待队列上,通过调用 AQS 方法,遍历等待队列实现
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
// 获取等待队列信息
public final int getQueueLength() {
return sync.getQueueLength();
}
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
除了上述方法,ReentrantLock
中还有以下三个涉及到条件队列 (Condition) 的特殊方法:
// 判断条件队列上是否有处于 CONDITION 状态的节点
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
// 遍历条件队列,返回条件队列上处于 CONDITION 状态的节点数量
public int getWaitQueueLength(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
// 获取条件队列上处于 CONDITION 状态的所有节点内线程
protected Collection<Thread> getWaitingThreads(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}