ReentrantLock源码分析
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中存在一个全局的抽象同步器属性,然后通过构建方法来进行初始化,通过fair参数来指定到底是使用公平同步器还是非公平同步器,默认情况下是使用非公平同步器。
同时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;
}
}
// 锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还尝试获取锁,那么就累加同步状态的值,这种情况也返回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;
// 如果锁释放后,同步状态的值为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()方法,该方法需要由抽象同步器的子类来进行实现,ReentrantLock的lock()方法将会调用抽象同步器声明的lock()方法。
同时抽象同步器提供了nonfairTryAcquire()方法,如果同步状态的值为0,表示锁没有被线程所持有,则通过CAS尝试获取锁,如果获取锁成功则返回true,同时如果同步状态的值不为0,表示锁已经被线程所持有了,同时如果当前线程就是拥有锁的线程,它还进行加锁,那么就累加同步状态的值,这种情况也会返回true,否则返回false, 该方法会在ReentrantLock的tryLock()方法以及非公平同步器的tryAcquire()方法中被调用。
同时抽象同步器已经重写了AQS的tryRelease()方法,因此抽象同步器的子类还需要重写AQS的tryAcquire()方法。
剖析非公平同步器
/**
* 非公平同步器(继承抽象同步器)
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 实现抽象同步器声明的lock()方法
*/
final void lock() {
// 直接通过CAS尝试获取锁,如果失败则调用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() {
// 直接调用acquire()方法
acquire(1);
}
/**
* 重写AQS的tryAcquire()方法
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果同步状态为0,同时当前线程就是等待队列中头节点的后继节点所封装的线程,或者当前等待队列为空或者只有一个节点,那么才会通过CAS尝试获取锁(这个判断用于保证是公平锁)
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.当线程要获取锁时,可以直接调用ReentrantLock的lock()和tryLock()方法。
2.如果调用lock()方法,那么将会调用非公平同步器的lock()方法,该方法会通过CAS尝试获取锁,如果获取锁成功则直接返回,做自己的事情,否则将会调用AQS的acquire()方法。
3.AQS的acquire()方法又会调用非公平同步器的tryAcquire()方法,该方法直接调用抽象同步器的nonfairTryAcquire()方法,如果同步状态的值为0,表示锁没有被线程所持有,则通过CAS尝试获取锁,如果获取锁成功则返回true,同时如果同步状态的值不为0,表示锁已经被线程所持有,如果当前线程就是拥有锁的线程,它还进行加锁,那么就累加同步状态的值,这种情况也会返回true,否则返回false。
4.如果调用tryLock()方法,那么将会调用抽象同步器的nonfairTryAcquire()方法,通过CAS尝试获取锁。
5.当线程要释放锁时,将会调用ReentrantLock的unlock()方法,该方法将会直接调用AQS的release()方法,该方法又会调用抽象同步器的tryRelease()方法,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。
公平模式下的总结
1.当线程要获取锁时,可以直接调用ReentrantLock的lock()和tryLock()方法。
2.如果调用lock()方法,那么将会调用公平同步器的lock()方法,该方法直接调用AQS的acquire()方法。
3.AQS的acquire()方法又会调用公平同步器的tryAcquire()方法,同时该方法中只有当同步状态的值为0,同时当前线程是等待队列中头节点的后继节点所封装的线程,或者当前等待队列为空或只有一个节点时,才会通过CAS尝试获取锁,如果获取锁成功则返回true,同时如果同步状态的值不为0,表示锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还进行加锁,那么累加同步状态的值,这种情况也会返回true,否则返回false。
4.如果调用tryLock()方法,那么将会调用抽象同步器的nonfairTryAcquire()方法,通过CAS尝试获取锁。
5.当线程要释放锁时,将会调用ReentrantLock的unlock()方法,该方法将会直接调用AQS的release()方法,该方法又会调用抽象同步器的tryRelease()方法,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。
FAQ
关于ReentrantLock的tryLock()方法
ReentrantLock的tryLock()方法是非公平的,因为无论在什么模式下,该方法都会调用抽象同步器的nonfairTryAcquire()方法,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,被其他线程直接调用了tryLock()方法获取了锁,那么被唤醒的这个线程又只能再进入阻塞状态,这就是非公平的体现。
关于获取了锁的线程能否再进行加锁?
是可以的,因为无论在非公平还是公平模式下,tryAcquire()方法中都会有这么一个判断,如果同步状态的值不为0,表示锁已经被线程所持有,同时如果当前线程就是拥有锁的线程,它还进行加锁,那么累加同步状态的值,这种情况方法也会返回true,同时在抽象同步器的tryRelease()方法中,如果线程释放锁后,同步状态的值为0,则返回true,否则说明线程加了不止一把锁,然后它又没有释放全,这种情况会更新同步状态的值,然后返回false,只有当线程把它加的锁都释放后,tryRelease()方法才会返回true。
非公平锁是如何实现非公平的?
主要体现在非公平同步器的lock()方法,当线程要进行加锁时,并没有直接调用AQS的acquire()方法(虽然acquire也会调用tryAcquire),而是先通过CAS尝试获取锁,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,被其他线程直接调用了lock()方法获取了锁,那么被唤醒的这个线程又只能再进入阻塞状态,这就是非公平的体现。
公平锁是如何实现公平的?
主要体现在公平同步器的lock()和tryAcquire()方法,首先lock()方法直接调用AQS的acquire()方法,并没有像非公平同步器的lock()方法一样,先通过CAS尝试获取锁,然后在tryAcquire()方法中,只有当同步状态的值为0,同时当前线程就是等待队列中头节点的后继节点所封装的线程,或者当前等待队列为空或只有一个节点时,才会通过CAS尝试获取锁,因此当线程释放锁时,需要唤醒离头节点最近的同时等待状态不为CANCELLED的后继节点,然后在该节点尝试获取锁之前,被其他线程调用了lock()方法进行加锁,由于lock()方法直接调用AQS的acquire()方法,然后acquire()方法又调用公平同步器的tryAcquire()方法,虽然此时同步状态的值为0,但是当前线程并不是等待队列中头节点的后继节点所封装的线程,因此该线程也只能封装成Node节点,然后加入到等待队列当中。