ReentrantLock是一个可重入的互斥锁,ReentrantLock将所有的锁操作都委派给了实现AQS的内部类Sync上,同时定义了Sync的两个子类分别提供对公平锁和非公平锁的支持,默认使用非公平锁。
1、Sync,NofairSync和FairSync
ReentrantLock基于AQS独占锁的实现,Sync类继承了AQS,主要实现了其尝试加锁的tryAcquire
方法,尝试释放锁的tryRelease
方法。Sync类实际是非公平实现,NofairSync类只是套了Sync类的壳子,公平锁实现FairSync主要是加锁时有所不同。
ReentrantLock默认为非公平锁实现,调用其有参构造指定fair为true则为公平锁实现。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 非公平锁加锁逻辑
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//如果是无锁状态直接抢锁
if (compareAndSetState(0, acquires)) {
//抢锁成功则设置锁的占用线程
setExclusiveOwnerThread(current);
return true;
}
}
//当前锁被占用但是占用线程和当前线程是同一线程则是锁重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
/**
* 尝试释放锁
* @param releases
* @return
* true 当前线程已经完全释放锁
* false 当前线程未完全释放锁
*/
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
//当前值- 释放的值
int c = getState() - releases;
//当前线程不是持锁线程直接抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//是佛已经完全释放锁
boolean free = false;
if (c == 0) {
//说明当前线程已经完全释放锁
free = true;
//将锁的独占线程设置为null
setExclusiveOwnerThread(null);
}
//更新state的值
setState(c);
return free;
}
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
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;
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
@ReservedStackAccess
/**
* 尝试抢锁 返回是否抢锁成功
* 返回true
* 1、AQS处于无锁状态且当前线程为第一个请求锁的线程,CAS抢锁成功
* 2、AQS处于有锁状态,但当前线程和持有锁的线程为同一线程,为锁重入
* 返回false
* 1、AQS处于无锁状态且当前线程为第一个请求锁的线程,CAS抢锁失败
* 2、AQS处于有锁状态,并且当前线程不是持有锁的线程
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//c == 0 表明当前AQS处于无锁状态
if (c == 0) {
/**
*
* 条件1:!hasQueuedPredecessors() 因为当前为公平锁 任何时候都要检查一下 队列中是否有在当前线程前的等待者
* true 说明当前线程前面没有等待者,直接尝试获取锁
* false 说明当前线程前面有等待者,当前线程需要入队等待
* 条件2:compareAndSetState(0, acquires) 尝试CAS获取锁
* 前置条件(AQS处于无锁状态且当前线程为第一个请求锁的线程)
* true 当前线程CAS抢锁成功
* false 当前线程CAS抢锁失败
*
*/
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 将持锁线程对象设置为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
//c != 0 表明当前AQS锁被占用
else if (current == getExclusiveOwnerThread()) {
//如果当前线程和持有锁的线程相同 则为锁重入
int nextc = c + acquires;
//越界判断:重入的深度很深 会导致 nextc < 0 ,int值达到最大值 再加一就为负数了
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
/**
* 执行到此处的情况:
* 1、state == 0 ,CAS失败
* 2、state >0 且当前线程不是持锁线程
*/
return false;
}
}
2、加锁流程
只贴ReentrantLock相关代码,AQS相关代码参考AQS源码分析
AQS源码分析
ReentrantLock#lock
public void lock() {
sync.acquire(1);
}
AQS#acquire
/**
* 阻塞等待获取锁
* 1、AQS为无锁状态,第一次直接尝试抢锁然后成功
* 2、AQS为无锁状态且AQS队列没有线程等待锁但是CAS抢锁失败 或者 AQS队列有线程等待锁,将当前线程封装成节点加入等待队列,
* 然后在acquireQueued方法中发现前置节点为头节点,再次尝试抢锁,然后成功了
* 3、AQS队列有线程等待锁,将当前线程封装成节点加入等待队列,然后线程被挂起了,唤醒(可能外部线程调用unpark或者中断)后继续抢锁,如果失败则继续挂起,直到抢锁成功
* @param arg
*/
public final void acquire(int arg) {
/**
*条件1: !tryAcquire(arg) 尝试获取锁 子类实现
* 返回true 第一次尝试获取锁失败
* 返回false 第一次尝试获取锁成功
* 条件2: acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
* 前置条件(第一次尝试获取锁失败)
* addWaiter(Node.EXCLUSIVE) 将当前线程封装成node然后入队
* acquireQueued 1、挂起当前线程 2、执行唤醒后相关逻辑
* 返回true 挂起过程中线程被中断或者唤醒过
* 返回false 未被中断过
*
*/
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){
/**
* 如果线程被挂起后被中断退出 再次设置中断标记
*/
selfInterrupt();
}
}
2.1、公平锁加锁
ReentrantLock是一个可重入的互斥锁,所以有锁状态下,如果持锁线程和当前线程为同一线程,则为锁重入。公平锁抢锁时,需要满足两个条件,一是当前为无锁状态,二是队列中无等待线程,否则不能抢锁。
/**
* 尝试抢锁 返回是否抢锁成功
* 返回true
* 1、AQS处于无锁状态且当前线程为第一个请求锁的线程,CAS抢锁成功
* 2、AQS处于有锁状态,但当前线程和持有锁的线程为同一线程,为锁重入
* 返回false
* 1、AQS处于无锁状态且当前线程为第一个请求锁的线程,CAS抢锁失败
* 2、AQS处于有锁状态,并且当前线程不是持有锁的线程
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//c == 0 表明当前AQS处于无锁状态
if (c == 0) {
/**
*
* 条件1:!hasQueuedPredecessors() 因为当前为公平锁 任何时候都要检查一下 队列中是否有在当前线程前的等待者
* true 说明当前线程前面没有等待者,直接尝试获取锁
* false 说明当前线程前面有等待者,当前线程需要入队等待
* 条件2:compareAndSetState(0, acquires) 尝试CAS获取锁
* 前置条件(AQS处于无锁状态且当前线程为第一个请求锁的线程)
* true 当前线程CAS抢锁成功
* false 当前线程CAS抢锁失败
*
*/
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
// 将持锁线程对象设置为当前线程
setExclusiveOwnerThread(current);
return true;
}
}
//c != 0 表明当前AQS锁被占用
else if (current == getExclusiveOwnerThread()) {
//如果当前线程和持有锁的线程相同 则为锁重入
int nextc = c + acquires;
//越界判断:重入的深度很深 会导致 nextc < 0 ,int值达到最大值 再加一就为负数了
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
/**
* 执行到此处的情况:
* 1、state == 0 ,CAS失败
* 2、state >0 且当前线程不是持锁线程
*/
return false;
}
2.2、非公平锁加锁
和公平锁的可重入逻辑相同,有锁状态下,如果持锁线程和当前线程为同一线程,则为锁重入。非公平锁抢锁时,只需要一个条件,当前为无锁状态,不用关系是队列中无等待线程。
NonfairSync#tryAcquire
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
Sync#nonfairTryAcquire
/**
* 非公平锁加锁逻辑
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//如果是无锁状态直接抢锁
if (compareAndSetState(0, acquires)) {
//抢锁成功则设置锁的占用线程
setExclusiveOwnerThread(current);
return true;
}
}
//当前锁被占用但是占用线程和当前线程是同一线程则是锁重入
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
3、释放锁流程
ReentrantLock#unlock
public void unlock() {
sync.release(1);
}
AQS#release
/**
* 释放锁
* @param arg
* @return
*/
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
/**
* 条件1: h != null
* head是懒加载的,head为null说明没有发生锁竞争,当前线程拿锁时就为空队列,一直没有创建head节点。只有线程尝试拿锁时,发现为无锁状态,并且队列为空队列,
* 但是CAS抢锁时失败了,此时将当前线程封装为节点入队时首先就要创建一个head节点,然后将当前线程节点放在尾结点
*
* 条件2:h.waitStatus != 0
* 返回true 当前节点后面一定插入过其他节点 当前线程需要唤醒后继节点
*/
if (h != null && h.waitStatus != 0){
//唤醒最近的后继节点
unparkSuccessor(h);
}
return true;
}
return false;
}
Sync#tryRelease
/**
* 尝试释放锁
* @param releases
* @return
* true 当前线程已经完全释放锁
* false 当前线程未完全释放锁
*/
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
//当前值- 释放的值
int c = getState() - releases;
//当前线程不是持锁线程直接抛异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//是否已经完全释放锁
boolean free = false;
if (c == 0) {
//说明当前线程已经完全释放锁
free = true;
//将锁的独占线程设置为null
setExclusiveOwnerThread(null);
}
//更新state的值
setState(c);
return free;
}