本文是对 ReentrantLock
源码的分析以及总结,详细介绍了其是实现公平与非公平锁的加解锁的原理、其与 AbstractQueuedSynchronizer
的关联。
简介
可重入锁是 JUC 包中提供的一种可重入的互斥锁,其实现基于 [[AbstractQueuedSynchronizer]] , Java 源码的注释很好的说明了 ReentrantLock
是什么: ReentrantLock
和 Java synchronized
的语义一致,但包含更多的功能。
A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.
代码结构
AQS 和其实现类之间的关系属于模板方法:AQS 提供基础的算法框架,实现类实现抽象方法来实现自己独特的功能和语义。 AbstractQueuedSynchronizer
的源码注释中有这么一段话,很好地说明了 AQS 与 JUC 包中具体同步器之间的关系:
Subclasses should be defined as non-public internal helper classes that are used to implement the synchronization properties of their enclosing class. Class AbstractQueuedSynchronizer does not implement any synchronization interface. Instead it defines methods such as acquireInterruptibly that can be invoked as appropriate by concrete locks and related synchronizers to implement their public methods.
有个这样一个前提,我们才能更好地理解 RenentrantLock
的实现和原理。
ReentrantLock
的整体结构如下:
ReentrantLock
实现了Lock
接口,使用者仅需调用lock
和unlock
接口即可实现加解锁。- 抽象内部类
Sync
继承了AbstractQueuedSynchronizer
,Sync
类实现了不分 AQS 中定义的模板方法,比如tryRelease
和isHeldExclusively
- 为了支持公平锁和非公平锁两种语义,
RenentrantLock
内部定义了FairSync
和NonfairSync
两个继承自Sync
的类,FaireSync
和NonfairSync
实现 AQS 中定义的tryAcquire
模板方法,实现了公平锁及非公平锁的加锁逻辑。
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync; // 同步器变量
// Sync 继承自 AQS,实现了 AQS 中定义的模板方法
abstract static class Sync extends AbstractQueuedSynchronizer {...}
// 非公平锁实现类
static final class NonfairSync extends Sync {...}
// 公平锁实现类
static final class FairSync extends Sync {...}
}
源码分析
构造函数
ReentrantLock
在构造函数时可以传入一个不二参数,来生成一个公平锁实例,默认情况下的 ReentrantLock
是非公平锁:
public class ReentrantLock implements Lock, java.io.Serializable {
/**
* Creates an instance of {@code ReentrantLock}.
* This is equivalent to using {@code ReentrantLock(false)}.
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
}
非公平锁加锁实现
非公平锁的加锁过程如下:
- 调用
NonfairSync
实现的lock
方法,该方法将调用 AQS 中的compareAndSetState
,看能否直接获取锁,如果该方法返回true
那么线程将设置当前线程为ReentrantLock
的独占持有者; - 如果 AQS
state
状态变量不为 0,则调用 AQS 中的acquire
方法; - AQS 的
acquire
会调用实现类的tryAcquire
方法,也就是NonfairSync
自己实现的tryAcquire
方法,该方法会调用Sync
中定义的nonfairTryAcquire
Sync
中的nonfairTryAcquire
将尝试原子性地更新 AQS 中的state
状态变量,这里有两种情况:- 当前线程是
ReentrantLock
的持有者,那么只需更新 AQS 的state
状态变量即可,并返回true
表示成功获取锁; - 如果当前线程不是可重入锁的持有者,则判断当前是否可以获取锁,并尝试原子性更新 AQS 的
state
状态变量,成功则返回true
,否则返回false
- 当前线程是
- 失败后将返回到 AQS 的
acquire
中,将当前线程加入到 AQS 的等待队列里。
public class ReentrantLock implements Lock, java.io.Serializable {
abstract static class Sync extends AbstractQueuedSynchronizer {
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
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;
}
}
// 非公平锁实现类
static final class NonfairSync extends Sync {
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
}
公平锁加锁实现
公平锁加锁过程如下:
- 调用
FairSync
的lock
方法,其会调用 AQS 中的acquire
方法; - AQS
acquire
方法调用具体实现类,也就是FairSync
自己的tryAcquire
方法尝试获取锁; tryAcuire
首先会判断能否获取锁,也就是判断 AQSstate
变量的值:- 如果为 0,则判断 AQS 的等待队列中是否有已经在排队的线程,有的话则返回
false
,回到 AQS 的acquire
中进行后续入队操作。 - 如果不为 0,那么判断当前所是否是
ReentrantLock
的持有者线程,是则原子性更新 AQS 的state
变量,否则返回false
,回到 AQS 的acquire
中进行后续入队操作。
- 如果为 0,则判断 AQS 的等待队列中是否有已经在排队的线程,有的话则返回
public class ReentrantLock implements Lock, java.io.Serializable {
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
}
}
解锁实现
无论是公平锁还是非公平锁实现,解锁代码逻辑是一样的,因此是由 Sync
类来实现的:
- 计算解锁后
state
的变量值c
- 防御性判断当前线程是否为
ReentrantLock
的持有者线程,如果不是那么就有问题了,抛异常终止 - 判断
c
是否为 0,如果是则原子性地设置可重入锁的持有者线程为null
- 原子性更新 AQS 的
state
- 返回锁是否被释放
public class ReentrantLock implements Lock, java.io.Serializable {
abstract static class Sync extends AbstractQueuedSynchronizer {
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
}
}
可重入性
从源码可以看出, ReentrantLock
的可重入性,来源于加锁时对 AQS state
变量值的判断:如果 state
值不为 0,则判断当前线程是否为锁的持有线程,如果是,则直接原子性的将 state
的值加一。
解锁时, unlock
方法调用 Sync
内的 tryRelease
方法,原子性的将 AQS 中的 state
值减一,如果 state
变为 0,则将 ReentrantLock
的持有者线程设为 null
。
这也就要求 ReentrantLock
的加锁和解锁必须是成对出现和匹配的,否则,因为少调用了 tryRelease
方法,AQS 的 state
不为 0,将导致排队的线程永远也无法获取锁。正如源码注释说的那样, ReentrantLock
的最佳实践:
It is recommended practice to always immediately follow a call to lock with a try block, most typically in a before/after construction such as:
class X {
private final ReentrantLock lock = new ReentrantLock ();
// ...
public void m () {
lock. lock (); // block until condition holds
try {
// ... method body
} finally {
lock. unlock ()
}
}
}
Reference
- Juc 源码