上一篇文章中,我们介绍了 ReentrantLock 的用法,他就是通过 AQS 来实现的,也是 AQS 独占模式的典型示例。 接下来我们就来看看 ReentrantLock 是如何实现的。
根据类图,我们可以看到 ReentrantLock 拥有三个内部类,分别是。 上文类图中,Sync 实现了解锁的 tryRelease 与 tryReleaseShared 方法,而其子类 NonfairSync(非公平锁) 与 FairSync(公平锁) 则实现了 tryAcquire 与 tryAcquireShared 及 isHeldExclusively 方法,从而让整个锁的实现层次更加清晰。
上文中我们介绍过 ReentrantLock 的加锁与解锁方法,我们来看看他们究竟是怎么实现的。
加锁 — lock
可中断锁 — lockInterruptibly
尝试获取锁 — tryLock
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
可以看到,所有的加锁解锁方法均直接调用了内部类 Sync。
根据类图,我们可以看到,ReentrantLock 拥有一个内部类 Sync,他是 AQS 的子类,同时,ReentrantLock 还拥有另外两个内部类 — FairSync 和 NonfairSync,他们分别提供了公平锁与非公平锁的加锁方法。
公平锁是通过 Sync 的子类 FairSync 实现的,他复写了父类的 abstract lock 方法。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
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;
}
}
lock 方法直接调用了 AQS 的 acquire 方法,这个方法在之前的文章中已经介绍过了,这里不再赘述。 同时,他实现了 Sync 没有实现的 tryAcquire 方法,这个方法就是实际上的公平锁的加锁方法。 1. 此前我们看到,AQS 中有一个 state 成员,这个 int 类型的成员就是锁状态的标识,为 0 则表示当前锁处于空闲状态,否则说明锁处于非空闲状态 2. 同时我们提到过,AbstractQueuedSynchronizer 继承自 AbstractOwnableSynchronizer,AbstractOwnableSynchronizer 中 exclusiveOwnerThread 字段记录了当前正在占用锁的线程 了解了上述两点,公平锁的加锁代码就非常简单了。
通过 AbstractQueuedSynchronizer 提供的原子操作 compareAndSetState 实现了加锁的并发控制,当然他最终调用的是 UNSAFE 类的成员方法:
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
NonfairSync 类提供了非公平锁的加锁方法。
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 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);
}
}
可以看到,非公平锁的加锁与公平锁的加锁有一点不同,那就是如果当前锁状态是空闲状态,那么就可以直接获取锁,而公平锁显然是不能这么做的,因为公平锁必须要先判断 AQS 同步队列中是否有先入队的正在等待获取锁的线程,否则直接获取就打破了公平锁的原则了。 同样的,真正的加锁方法仍然是 Sync 所没有实现的 tryAcquire 方法,NonfairSync 中直接调用了父类的 nonfairTryAcquire 方法,也就是说,虽然 Sync 并没有直接实现 tryAcquire 方法,但他其实已经提供了一个非公平锁的加锁实现,那就是 nonfairTryAcquire 方法,我们来看看 Sync 的 nonfairTryAcquire 方法做了什么。
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;
}
乍一看,这个方法和我们上面介绍过的公平锁加锁方法似乎没什么区别,仔细一看,还是我们上面讲到的,非公平锁与公平锁最大的区别在于,他在获取锁之前不需要先去判断 AQS 同步队列中是否有正在等待获取锁的节点,这样,非公平锁的实现看起来就更简单了。
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;
}
这段解锁的代码看上去非常简单,既然加锁的时候对 AQS 的状态值进行了增加操作,那么在解锁的时候就必须进行逆向的减操作了,同时,如果此时锁已经被释放,那么就需要把 AbstractOwnableSynchronizer 类中的 exclusiveOwnerThread 成员置空。