ReentrantLock 源码解析
前言
-
ReentrantLock 实现了 Lock 接口,内部基于 AQS 实现。所以想要弄懂 ReentrantLock ,一定要先弄明白 AQS。
-
ReentrantLock 是可重入锁,同一个线程能够多次获取到同一个 ReentrantLock 锁。
-
ReentrantLock 类采用的是 AQS 技术中的独占模式,实现了 AQS 的 3 个关键方法:tryAcquire(),tryRelease(),isHeldExclusively()方法。
- 一个线程可以多次通过 ReentrantLock 的 lock()方法获取锁,但是要想完全释放锁,就必须调用相同次数的 unlock()方法。
- ReentrantLock 对同一个线程的可重入性是通过内部的计数器实现的,同一个线程调用lock()方法加锁,计数器就会 + 1。
- 同一个线程调用 unlock()方法释放锁,计数器就会 - 1。
-
ReentrantLock 的内部,有两个抽象类:NonFairSync 和 FairSync,二者都继承了 Sync 类,而 Sync 类继承了 AQS 。
- NonFairSync :表示的是非公平模式。
- FairSync:表示的是公平模式。
-
ReentrantLock 和 AQS 的关系图:
一、字段分析
private final Sync sync:
ReentrantLock 只有这一个字段,代表了公平锁或者非公平锁,是 AQS 的子类,Sync 是ReentrantLock 内部真正用来操作的类。可以想象 ReentrantLock 只是一个壳,所有关于ReentrantLock 的操作,其实在 ReentrantLock 内部都是对 sync 进行操作的。
二、内部类分析
1、Sync
- state:继承自 AQS,在 ReentrantLock 中的含义为:
0:当前锁没有被任何线程持有。
1:当一个线程第一次获取该锁时,会尝试使用CAS设置state的值为1。
> 1:当一个线程获取锁后,再次调用 lock(),每次调用 state + 1。每次调用 unlock()state - 1,当减为 0 时,该线程释放了锁。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
//抽象的获取锁的方法,由子类实现
abstract void lock();
//非公平锁的获取锁方法
//acquires:传入的是 state 值,传入的是 1
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取状态值
int c = getState();
//状态值 = 0 ,说明当前锁没有被任何线程持有
if (c == 0) {
//使用 CAS 尝试将 state 设为 1
if (compareAndSetState(0, acquires)) {
//设置成功后,表示获取到了锁,将 锁持有的线程设置为当前线程
setExclusiveOwnerThread(current);
//加锁成功
return true;
}
}
//如果 state = 0,说明已经有线程持有锁了,判断是否是当前线程对象自己持有的
else if (current == getExclusiveOwnerThread()) {
//当前线程已经持有锁了,将 state + 1
int nextc = c + acquires;
//state 不停地 + 1,导致溢出了。重入次数过多
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
//设置state
setState(nextc);
//可重入获取锁成功
return true;
}
//锁已经被其他线程持有,当前线程获取锁失败
return false;
}
//释放锁
//releases :传入的是 state,ReentranLock 释放锁传入的是 1
protected final boolean tryRelease(int releases) {
//计算释放锁后的state值
int c = getState() - releases;
//判断占用锁的是不是当前线程,如果不是,则报错
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//记录是否释放成功
boolean free = false;
//如果 state == 0,则释放锁成功
if (c == 0) {
free = true;
//锁释放成功,将锁的持有线程设置为空
setExclusiveOwnerThread(null);
}
//更新状态值
setState(c);
//返回是否释放成功
return free;
}
//判断持有锁的线程是否为当前线程
//true:持有锁的线程是当前线程
//false:持有锁的线程不是当前线程
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
//创建ConditionObject,用来关联条件队列
final ConditionObject newCondition() {
return new ConditionObject();
}
//获取持有锁的线程
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
//如果持有锁的线程为当前线程,返回 state,否则返回 0
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
//判断锁是否空闲
//true:空闲
//false:已被获取
final boolean isLocked() {
return getState() != 0;
}
}
2、FairSync
- 公平锁的实现。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
//获取锁
final void lock() {
acquire(1);
}
//获取公平锁
protected final boolean tryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取状态值
int c = getState();
//如果状态值为0,说明锁是空闲的,可以被获取
if (c == 0) {
//hasQueuedPredecessors():判断当前线程所在的节点(同步队列中)是否还有前驱节点
//true:有,false:同步队列为空(还未创建同步队列获只有空的head节点)或当前节点为头结点(指的是head的next节点)
//所以翻译为:没有前驱节点 且 使用cas方式设置 state 为 1,说明获取锁成功
//因为如果有前驱节点,由于是公平锁,需要先加入到同步队列的队尾进行排队,下一个尝试获取资源的是头结点代表的资源
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
//获取锁资源成功,设置锁拥有的线程为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
//否则判断锁拥有的线程是否为当前线程,true说明是进行锁的重入操作,将 state + 1
else if (current == getExclusiveOwnerThread()) {
//计算 state,就是 state + 1
int nextc = c + acquires;
//判断是否溢出
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//更新state
setState(nextc);
return true;
}
//否则获取锁失败
return false;
}
}
3、NonfairSync
-
非公平锁的实现。
static final class NonfairSync extends Sync { private static final long serialVersionUID = 7316153563782823691L; //获取锁 final void lock() { //使用cas的方式将state设置为1,成功说明锁未被持有,直接获取锁成功 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else //失败说明锁已经被持有,使用非公平的方式尝试获取锁 acquire(1); } //尝试获取锁资源,采用的是非公平的方式 protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); } }
三、方法分析
1、构造方法
//无参构造函数,默认使用的是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//有参构造函数
//true:公平锁
//false:非公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
2、其他方法
//获取锁,调用的其实是 sync 的方法(忽视中断)
public void lock() {
sync.lock();
}
//获取锁,调用的其实是 sync 的方法(不忽视中断)
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//尝试获取锁(非公平锁)
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
//尝试获取锁,获取失败后进入 doAcquireNanos 方法,如果时间超过了 timeout,则不进入同步队列了
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
//释放锁,调用的其实是 sync 的方法(忽视中断)
public void unlock() {
sync.release(1);
}
//创建 Condition ,其实调用的是sync的方法,创建是 AQS 的 ConditionObject 对象
public Condition newCondition() {
return sync.newCondition();
}
//获取state,如果锁是空闲的则返回 0
public int getHoldCount() {
return sync.getHoldCount();
}
//true:获取到锁的线程为当前线程
//false:获取到锁的线程不为当前线程
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
//true:锁已被获取
//false:锁空闲
public boolean isLocked() {
return sync.isLocked();
}
//方法都比较简单
...
总结
-
ReentrantLock 的代码比较简单,更多的实现在 AQS 中,结合 ReentrantLock 和 AQS 的源码,总结下ReentrantLock 获取锁和释放锁的整个流程。
-
公平模式下获取锁:lock()
-
非公平模式下获取锁:lock()
- 非公平模式下释放锁:unlock()和 公平模式下释放锁:unlock()执行方法一样的。
公平模式和非公平模式下获取锁的区别:体现在 lock 方法和 tryAcquire方法
-
公平模式下源码:
-
非公平模式下源码:
-
可以发现,
非公平锁模式下,如果锁是空闲状态可直接获取锁(lock方法中)。而公平方法中没有。并且在获取锁失败后,调用 tryAcquire 方法时,检查 state == 0 (说明锁是空闲的),非公平模式下是直接尝试获取锁
,而公平模式是检查同步队列中是否还有节点,有的话则不获取锁,没有才获取锁。