ReentrantLock比较常用的一个锁,基于AQS实现,支持可重入性,公平锁和非公平锁两种模式。
ReentrantLock的可重入性是通过对同步状态值state进行累加实现;ReentrantLock有三个内部类,分别是Sync,NonfairSync,FairSync他们的继承关系如下图所示,ReentrantLock持有一个Sync类型的引用,根据多态的思想,在公平锁和非公平锁模式下,分别将子类FairSync和NonfairSync对象引用赋值给他,这两个子类同步器中实现了具体的获取锁策略,来实现公平锁和非公平锁。
建议看完AQS关键源码再看来这部分,就会非常简单,当然不看也影响不大。
- 基本字段
private static final long serialVersionUID = 7373984872572414699L; //用于序列化,以后略去该字段
private final Sync sync; //NonfairSync和FairSync的基类引用,通过多态来分别实现公平锁和非公平锁
- lock方法
public void lock() {
sync.lock();
}
由于这里用到了多态,所以我们分别分析公平锁和非公平的lock方法。
FairSync的lock方法:
final void lock() {
acquire(1);
}
直接调用了AQS的acquire方法,acquire方法是AQS很重要的一个函数,这里不做分析,读者只需要知道这个acquire方法里会首先调用一个tryAcquire方法,而这个方法是AQS留给我们来重写的,FairSync的重写的tryAcquire方法:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
/**
* C == 0是首次获取锁
* !hasQueuedPredecessors()保证竞争锁的线程一定是同步队列的头结点
* 由于if短路,其他结点会直接返回,所以是公平锁,按照FIFS的顺序,只允许队头也就是最早来的节点竞争锁
*/
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { //可重入实现,由于这里已经持有锁了,所以不需要在做同步了
int nextc = c + acquires; //对同步器状态值,不断增加来实现可重入
if (nextc < 0) //int溢出
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false; //获取锁失败
}
}
接下来分析NonfairSync的lock方法:
final void lock() {
if (compareAndSetState(0, 1))//进行一次快速抢占锁
setExclusiveOwnerThread(Thread.currentThread());
else //抢占失败的线程,调用AQS的acquire方法,继续进行竞争锁
acquire(1);
}
同理,NonfairSync也重写了AQS的tryAcquire方法,如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
这个nonfairTryAcquire是Sync类提供的一个内部方法,因为这个nonfairTryAcquire方法不仅在这里被使用,还在tryLock方法中使用,所以作者将重复的代码封装成了一个函数。nonfairTryAcquire函数代码如下:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
/**
* 这里通过CAS保证只有一个线程能获取锁成功,但是会让所有线程进行竞争
* 与先后顺序无关,是非公平锁
*/
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { //可重入支持
int nextc = c + acquires;
if (nextc < 0) // int溢出,一般来说不会重入这么多次
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
可以看到nonfairTryAcquire里并没有hasQueuedPredecessors这个限制,所以是非公平的。
- unlock方法
public void unlock() {
sync.release(1);
}
对于公平锁和非公平所来说,释放锁的逻辑没有区别,所以在父类Sync中重写了AQS的tryRelease方法,这里是先调用了AQS的release方法(因为AQS是Sync的父类),然后release方法中首先调用了tryRelease方法,同样这是提供给用户来重写的方法,如下:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread()) //如果非持有线程来释放锁,直接抛出异常
throw new IllegalMonitorStateException();
boolean free = false;
/**
* 由于可重入,所有一次释放可能c还不等于0,直到c=0才会真正释放锁
* 这点来保证n次重复获取锁,必须也要进行n次释放
*/
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
- tryLock方法
之前提到过tryLock方法,tryLock方法是进行一次快速获取锁尝试,所以肯定是非公平的,不论是否成功都立刻返回,代码如下:
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
此外还有一些可响应中断的获取锁方法,超时返回的获取锁,是否公平,是否被当前线程持有锁等函数,比较简单,就不在此赘述。