1 概述
ReentrantLock是基于AQS实现的一款独占锁,有公平锁和非公平锁两种模式。
默认是使用的非公平锁:
public ReentrantLock() {
sync = new NonfairSync();
}
也可指定模式:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
- 1
ReentrantLock实现了Lock接口; - 2
ReentrantLock定义了内部类Sync继承AQS:abstract static class Sync extends AbstractQueuedSynchronizer{....}; - 3
Sync是一个抽象类,有两个实现类NonfairSync/FairSync,分别用来实现非公平锁/公平锁。
官方示例:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
2 实现
本文仅从ReentrantLock的lock/unlock的实现,分析如何基于AQS实现一个独占锁。
根据AQS约定(可参考AQS源码分析及核心方法解析):
- 1 lock:实现
tryAcquire,并在lock时调用acquire; - 2 unlock:实现
tryRelease,并在unlock时调用release。
2 lock()
ReentrantLock.lock()调用了Sync.lock(),而Sync.lock()是一个抽象方法,由子类实现。
所以NonfairSync/FairSync两个类中,必然使用了acquire方法去获取锁,并实现了tryAcquire方法。
2.1 NonfairSync
2.1.1 lock
- 1 调用
compareAndSetState将state值由0置换为1,成功的话,说明当前没有其他线程持有锁,不去排队,直接持有。通过setExclusiveOwnerThread将当前线程设置为独占锁持有者。 - 2 否则通过
acquire去排队。
final void lock() {
if (compareAndSetState(0, 1)) // 如果当前state==0,则说明没有其他线程持有锁,CAS成功。
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
2.1.2 tryAcquire
NonfairSync.tryAcquire直接调用的Sync.nonfairTryAcquire。
nonfairTryAcquire是非公平的tryAcquire实现方式,不会通过AQS.hasQueuedPredecessors来判断是有线程在排队。
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// state == 0,说明没有其他线程持有锁。
if (compareAndSetState(0, acquires)) {
// 将当前线程设置为`独占锁持有者`,tryAcqire成功。
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
/*
如果当前线程正是`独占锁持有者`,叠加state,实现`可重入`,tryAcqire成功。
也就是说AQS的同步列表中,有多个当前线程的节点。
*/
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
2.2 FairSync
2.2.1 lock
没有做compareAndSetState尝试,直接将自己加入AQS的同步队列中。
final void lock() {
acquire(1);
}
2.2.2 tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { // state == 0,说明没有其他线程持有锁。
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
/*
`!hasQueuedPredecessors()` 说明AQS的同步队列中,没有比自己更优先的线程在等待
*/
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
/*
如果当前线程正是`独占锁持有者`,叠加state,实现`可重入`,tryAcqire成功。
也就是说AQS的同步列表中,有多个当前线程的节点。
*/
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
FairSync.tryAcquire比NonFairSync.tryAcquire多了一个!hasQueuedPredecessors()的判断,其他流程都是一样的。
3 unlock()
unlock操作,NonFairSync/FairSync中没有区分,直接在Sync中实现。
Sync中的unlock调用了AQS.release,并实现了tryRelease。
public void unlock() {
sync.release(1);
}
3.1 tryRelease
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 当前线程不是`独占锁持有者`,抛出异常。
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // c == 0 说明锁已经完全释放。
free = true;
// 将当前`独占锁持有者`置空。
setExclusiveOwnerThread(null);
}
// 更新state值
setState(c);
return free;
}
4 总结
NonFairSync/FairSync基本流程是一样的,不同的是:
- 1
NonFairSync在lock时,会先尝试compareAndSetState(0, 1)抢占锁,失败的话再进行acquire(1);FairSync直接进行acquire(1)排队。 - 2
FairSync在tryAcquire时,在判断compareAndSetState(0, acquires)的同时,多进行了一个hasQueuedPredecessors()的判断,用于判断同步队列中是否有比自己优先的线程。