上篇文章JUC并发编程基石AQS源码之结构篇我们整体了解了JUC下加锁类的代码结构,这篇我们来看下ReentrantLock的代码结构,之后讲解AQS源码也主要以这个类为主。
实现Lock接口
首先是实现了Lock接口,实现了加锁方法lock(),但是这里没有直接调用AQS的acquire方法,往下看
public class ReentrantLock implements Lock, java.io.Serializable {
public void lock() {
sync.lock();
}
定义内部类
先看一下内部类的继承关系,会发现比上一篇多了一层继承关系,我们来慢慢分析
定义了一个抽象内部类Sync继承了AQS,定义了一个抽象方法lock,但是会发现没有实现tryAcquire()方法,再往下看
public class ReentrantLock implements Lock, java.io.Serializable {
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* 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;
}
又一个内部类NonfairSync实现了Sync,实现了Sync类的lock()方法,并且实现了tryAcquire方法。lock方法里调用了AQS的acquire()方法。
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);
}
}
到这我们就能梳理出大致的调用流程了,其中绿色部分代表需要自己实现
为什么多了一层结构?因为ReentrantLock有两种锁类型,公平锁和非公平锁
公平锁:不能插队,如果当前有线程在等待锁,则直接进入排队队列,等待在它之前排队的所有线程执行完后再去获取锁。
非公平锁:不管当前是否有其他线程在排队等待锁,先尝试获取一次锁,如果获取到则执行代码逻辑,否则进入排队队列,等待在它之前排队的所有线程执行完后再去获取锁。
而实现不同锁类型在代码上的体现就是对AQS的tryAcquire()方法的不同实现,有几种锁类型就有几个内部类。
再看上面这张图就会明白,NonfairSync代表了非公平锁的实现。FairSync代表了公平锁的实现。
加锁类型不同会有不同的tryAcquire()方法实现,其他的解锁等操作的逻辑是一致的,所以根据代码的重用性思想,就抽象出了Sync类。所以我们看Sync类只把加锁的方法抽象了,其他的都是具体的实现。
再看一下公平锁FairSync的代码,也只是实现了加锁的方法。
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;
}
}
ReentrantLock默认的锁方式为非公平锁,构造方法中新建了NonfairSync的实例。所以在抽象类Sync默认给出了非公平锁的实现方法nonfairTryAcquire()。
//ReentrantLock构造方法
public ReentrantLock() {
sync = new NonfairSync();
}
到此ReentrantLock类的结构我们大体都了解了,这样我们再去看源码才会有的放矢,知道为什么会跳到这个类的这个方法来执行了,这也是看懂AQS源码的第一个关键。
如有不实,还望指正