一、ReentrantLock类继承关系
二、方法
1、加锁:lock()
首先看一下方法调用线路图,RenntrantLock默认是非公平锁。
1、公平锁
final void lock() {
// 调用AQS的acquire()
acquire(1);
}
// 排它模式下,尝试获得锁
public final void acquire(int arg) {
// tryAcquire 方法是需要实现类去实现的,实现思路一般都是 cas 给 state 赋值来决定是否能获得锁
if (!tryAcquire(arg) &&
// addWaiter 入参代表是排他模式
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
protected final boolean tryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取锁的状态,如果不为0,则说明锁别人持有
int c = getState();
// 没人获得锁
if (c == 0) {
// hasQueuedPredecessors 是实现公平的关键
// 会判断当前线程是不是队列中头结点的下一个节点(头节点是释放锁的节点)
// 如果是(返回false),条件符合,可获得锁
// 如果不是(返回true),则继续等待
// compareAndSetState(0, acquires)自旋判断,如果锁的计数器不是0
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前线程不是线程持有者,则返回false
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
// 溢出检查
throw new Error("Maximum lock count exceeded");
// 设置锁计数器值
setState(nextc);
return true;
}
return false;
}
2、非公平锁
1、lock()
final void lock() {
// CAS设置状态值
if (compareAndSetState(0, 1))
// CAS成功,将当前线程设置成获得锁的线程
setExclusiveOwnerThread(Thread.currentThread());
else
// 调用AQS的acquire(),再次尝试获得锁
// 失败会进入同步队列中
acquire(1);
}
2、tryAcquire()
// 直接使用的是 Sync.nonfairTryAcquire 方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
3、nonfairTryAcquire()
此方法与公平锁的差别主要是此处并不会去队列中判断自己是不是最早请求该锁的线程,可以理解是现实生活中的插队。
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 获取同步器的状态
int c = getState();
// 状态为0的话,说明同步器的锁没有人持有
if (c == 0) {
// 尝试CAS获得该锁,将AQS的状态从0设置为1
if (compareAndSetState(0, acquires)) {
// 设置当前线程为线程锁持有者
setExclusiveOwnerThread(current);
return true;
}
}
// 锁已经被人持有,需要判断当前线程是不是锁的持有者;是的话,则状态值+1
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
// 判断是否溢出了
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
// 抢锁失败,放入AQS阻塞队列
return false;
}
2、释放锁:unlock()
unlock 释放锁的方法,底层调用的是 Sync 同步器的 release 方法,release 是 AQS 的方法,分成两步:
- 调用Sync类的release方法,释放不成功返回false,调用步骤2
- 释放成功后,从同步队列中唤醒头结点的下一个节点,让其竞争锁
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 如果不是线程持有者释放锁会抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果同步器状态为为0,则清空锁持有线程
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// c不是0,当前线程已经拥有这个锁,即可重入锁,将重入次数-1.
setState(c);
return free;
}
tryRelease()方法是公平锁与非公平锁公用的。