ReentrantLock源码分析
ReentrantLock是独享锁,同时是基于AQS实现的,因此它内部肯定是通过自定义AQS独占模式下的同步器来实现独享锁,该同步器需要重写AQS提供的tryAcquire()和tryRelease()方法,只需要告诉AQS尝试获取同步资源和释放同步资源是否成功即可。
AQS子类需要定义以及维护同步状态的值,在ReentrantLock中使用state为0表示同步资源没有被线程所持有,使用state不为0表示同步资源已经被线程所持有。
ReentrantLock有公平和非公平两种模式,公平模式是指多线程按照申请锁的顺序来获取锁,非公平模式是指多线程并非按照申请锁的顺序来获取锁。
ReentrantLock的结构
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
/**
* 抽象同步器(AQS独占模式)
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
// ......
}
/**
* 非公平同步器
*/
static final class NonfairSync extends Sync {
// ......
}
/**
* 公平同步器
*/
static final class FairSync extends Sync {
// ......
}
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
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);
}
// 其它省略
}
可以发现ReentrantLock中定义了一个抽象同步器(Sync)、非公平同步器(NonfairSync)、公平同步器(FairSync),同时非公平同步器和公平同步器都继承抽象同步器。
同时ReentrantLock中存在一个全局的抽象同步器属性(sync),通过ReentrantLock的构建方法来进行初始化,并通过参数来指定是使用公平同步器还是非公平同步器,默认情况下是使用非公平同步器。
同时ReentrantLock中的核心方法,如lock()加锁方法,是调用抽象同步器声明的lock()方法,unlock()解锁方法,是调用AQS的release()方法,而tryLock()方法,是调用抽象同步器提供的nonfairTryAcquire()方法。
剖析抽象同步器
/**
* 抽象同步器(AQS独占模式)
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 声明了lock()方法,用于加锁
* ReentrantLock的lock()方法会调用抽象同步器声明的lock()方法
*/
abstract void lock();
/**
* 用于尝试获取锁,如果获取成功则返回true,并修改同步状态的值,否则返回false
* ReentrantLock的tryLock()方法会调用抽象同步器提供的nonfairTryAcquire()方法
*/
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取同步状态
int c = getState();
// 如果同步状态为0,表示锁没有被线程所持有
if (c == 0) {
// 通过CAS将同步状态设置为1,获取锁
if (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;
}
/**
* 重写AQS的tryRelease()方法,用于尝试释放锁,如果释放成功,则返回true,并修改同步状态的值,否则返回false
*/
protected final boolean tryRelease(int releases) {
// 获取锁释放后剩余的同步资源
int c = getState() - releases;
// 如果拥有锁的线程并非当前线程,则直接抛出异常(要求谁加的锁只能由谁进行解锁)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果锁释放后,state的值为0,则返回true,否则返回false(说明线程加了不止一把锁,然后又没有全释放)
if (c == 0) {
free = true;
// 清空拥有同步资源的线程
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
/**
* 判断当前线程是否持有锁
*/
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
/**
* 返回Condition实例,在某些场景下可以进行阻塞与唤醒
*/
final ConditionObject newCondition() {
return new ConditionObject();
}
/**
* 获取拥有锁的线程
*/
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
/**
* 返回当前线程拥有锁的个数
*/
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
/**
* 判断锁是否已经被线程所持有
*/
final boolean isLocked() {
return getState() != 0;
}
}
抽象同步器声明的lock()方法用于加锁,该方法需要由抽象同步器的子类,也就是非公平同步器和公平同步器来实现。
同时抽象同步器中提供了nonfairTryAcquire()方法,该方法会在ReentrantLock的tryLock()方法以及非公平同步器的tryAcquire()方法中被调用。
tryAcquire()方法由抽象同步器的子类来重写,而tryRelease()方法由抽象同步器来重写。
剖析非公平同步器
/**
* 非公平同步器(继承抽象同步器)
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 实现抽象同步器声明的lock()方法
*/
final void lock() {
// 直接通过CAS尝试获取锁,如果获取成功则将当前线程设置为拥有同步资源的线程,然后直接返回,否则调用AQS的acquire()方法
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
/**
* 重写AQS的tryAcquire()方法,直接调用抽象同步器的nonfairTryAcquire()方法
*/
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
非公平模式下的ReentrantLock的加锁,将会调用非公平同步器的lock()方法,该方法先通过CAS尝试获取锁,当获取失败时才会调用AQS的acquire()方法,该方法又会调用非公平同步器的tryAcquire()方法。
非公平模式下的ReentrantLock的解锁,将会调用AQS的release()方法,该方法又会调用抽象同步器提供的tryRelease()方法。
剖析公平同步器
/**
* 公平的同步器(继承抽象同步器)
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
/**
* 实现抽象同步器声明的lock()方法
*/
final void lock() {
// 直接调用AQS的acquire()方法
acquire(1);
}
/**
* 重写AQS的tryAcquire()方法
*/
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取同步状态
int c = getState();
// 如果同步状态为0,同时等待队列中头节点的后继节点封装的线程是当前线程,那么才会通过CAS将同步状态设置为1,表示获取锁(这个判断就是用于保证公平锁的)
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current); // 将拥有同步资源的线程设置为当前线程
return true;
}
}
// 如果同步状态不为0,也就是锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还尝试获取锁,那么就累加同步状态的值,然后返回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的加锁,将会调用公平同步器的lock()方法,该方法直接调用AQS的acquire()方法,该方法内部又调用公平同步器的tryAcquire()方法。
公平模式下的ReentrantLock的解锁,将会调用AQS的release()方法,该方法又会调用抽象同步器提供的tryRelease()方法。
非公平模式下的总结
1.当线程要获取锁时,可以直接调用lock()和tryLock()方法。
2.如果调用lock()方法,那么将会调用非公平同步器的lock()方法,该方法会先通过CAS尝试获取锁,如果获取锁成功则直接返回,否则将会调用AQS的acquire()方法。
3.AQS的acquire()方法又会调用非公平同步器的tryAcquire()方法,该方法直接调用抽象同步器的nonfairTryAcquire()方法,如果同步状态的值为0,则通过CAS尝试获取锁,如果获取锁成功则返回true,否则返回false,同时如果同步状态的值不为0,同时当前线程就是拥有锁的线程,那么允许它继续进行加锁,然后累加同步状态的值,这种情况也会返回true。
4.如果调用tryLock()方法,那么将会调用抽象同步器的nonfairTryAcquire()方法。
5.当线程要释放锁时,将会调用ReentrantLock的unlock()方法,该方法直接调用AQS的release()方法,该方法又会调用抽象同步器的tryRelease()方法,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,那么会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。
公平模式下的总结
1.当线程要获取锁时,可以直接调用lock()和tryLock()方法。
2.如果调用lock()方法,那么将会调用公平同步器的lock()方法,该方法直接调用AQS的acquire()方法。
3.AQS的acquire()方法又会调用公平同步器的tryAcquire()方法,该方法中只有当同步状态的值为0,同时等待队列中的头节点的后继节点封装的线程是当前线程时,才会通过CAS尝试获取锁,如果获取锁成功则返回true,否则返回false,同时如果同步状态的值不为0,同时当前线程就是拥有锁的线程,那么允许它继续进行加锁,然后累加同步状态的值,这种情况也会返回true。
4.如果调用tryLock()方法,那么将会调用抽象同步器的nonfairTryAcquire()方法。
5.当线程要释放锁时,将会调用ReentrantLock的unlock()方法,该方法直接调用AQS的release()方法,该方法又会调用抽象同步器的tryRelease()方法,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,那么会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。
FAQ
关于ReentrantLock的tryLock()方法
ReentrantLock的tryLock()方法是非公平的,因为无论在什么模式下,ReentrantLock的tryLock()方法总是调用抽象同步器的nonfairTryAcquire()方法,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,其他线程直接调用了tryLock()方法获取了锁,那么被唤醒的这个线程又只能再进入阻塞状态,这就是非公平的体现。
关于获取了锁的线程能否再进行加锁?
是可以的,因为无论在非公平模式下还是公平模式下,tryAcquire()方法当中都会有这么一个判断,也就是如果当前同步状态的值不为0,表示锁已经被线程所持有,同时当前线程就是拥有锁的线程,那么允许它继续进行加锁,然后累加同步状态的值,这种情况方法也会返回true,同时在抽象同步器重写的tryRelease()方法,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,那么会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。
非公平锁是如何实现非公平的?
主要体现在非公平同步器的lock()方法,当线程要进行加锁时,并没有直接调用AQS的acquire()方法,而是先通过CAS尝试获取锁,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,其他线程直接调用了lock()方法获取了锁,那么被唤醒的这个线程又只能再进入阻塞状态,这就是非公平的体现。
公平锁是如何实现公平的?
主要体现在公平同步器的lock()和tryAcquire()方法,首先lock()方法直接调用AQS的acquire()方法,并没有像非公平同步器的lock()方法一样,先通过CAS尝试获取锁,然后在tryAcquire()方法中,只有当同步状态的值为0,同时等待队列中的头节点的后继节点封装的线程是当前线程时,才会通过CAS尝试获取锁,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,其他线程调用了lock()方法进行加锁,由于lock()方法直接调用AQS的acquire()方法,然后acquire()方法又调用公平同步器的tryAcquire()方法,虽然判断到当前同步状态的值为0,但是当前线程并不是等待队列中头节点的后继节点封装的线程,因此该线程也只能封装成Node节点,然后加入到等待队列当中。