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) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}/*** 尝试释放锁* @param releases* @return* true 当前线程已经完全释放锁* false 当前线程未完全释放锁*/@ReservedStackAccessprotected final boolean tryRelease(int releases) {//当前值- 释放的值int c = getState() - releases;//当前线程不是持锁线程直接抛异常if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();//是佛已经完全释放锁boolean free = false;if (c == 0) {//说明当前线程已经完全释放锁free = true;//将锁的独占线程设置为nullsetExclusiveOwnerThread(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) // overflowthrow 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 当前线程未完全释放锁*/@ReservedStackAccessprotected final boolean tryRelease(int releases) {//当前值- 释放的值int c = getState() - releases;//当前线程不是持锁线程直接抛异常if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();//是否已经完全释放锁boolean free = false;if (c == 0) {//说明当前线程已经完全释放锁free = true;//将锁的独占线程设置为nullsetExclusiveOwnerThread(null);}//更新state的值setState(c);return free;}
