ReentrantLock源码解析

public ReentrantLock(boolean fair) {

sync = fair ? new FairSync() : new NonfairSync();

}

无参构造器默认构造是非公平的锁,有参构造器可以选择。从构造器中可以看出,公平锁是依靠FairSync 实现的,非公平锁是依靠 NonfairSync 实现的。

1.2.3 Sync

Sync 表示同步器,继承了 AQS;

1.2.3.1 nonfairTryAcquire方法

// 尝试获得非公平锁

final boolean nonfairTryAcquire(int acquires) {

final Thread current = Thread.currentThread();

int c = getState();

// 同步器的状态是0,表示同步器的锁没有人持有

if (c == 0) {

// 如果当前线程持有锁

if (compareAndSetState(0, acquires)) {

// 标记当前持有锁的线程是谁

setExclusiveOwnerThread(current);

return true;

}

}

// 如果当前线程已经持有锁了,同一个线程可以对同一个资源重复加锁,代码实的是可重入锁

else if (current == getExclusiveOwnerThread()) {

// 当前线程持有锁的数量 + acquires

int nextc = c + acquires;

if (nextc < 0)

throw new Error(“Maximum lock count exceeded”);

setState(nextc);

return true;

}

// 否则线程进入同步队列

return false;

}

  1. 通过判断 AQS 的 state 的状态来决定是否可以获得锁,0 表示锁是空闲的;

  2. else if 的代码体现了可重入加锁,同一个线程对共享资源重入加锁,底层实现就是把 state + 1,并且可重入的次数是有限制的,为 Integer 的最大值;

  3. 这个方法是非公平的,所以只有非公平锁才会用到,公平锁是另外的实现。

无参的 tryLock 方法调用的就是此方法,tryLock 的方法源码如下:

public boolean tryLock() {

// 入参数是 1 表示尝试获得一次锁

return sync.nonfairTryAcquire(1);

}

1.2.3.2 tryRelease方法

// 释放锁的方法,公平锁与非公平锁都可以使用

protected final boolean tryRelease(int releases) {

// 当前同步器的状态减去释放的个数,releases一般为1

int c = getState() - releases;

// 当前线程没有锁,报错

if (Thread.currentThread() != getExclusiveOwnerThread())

throw new IllegalMonitorStateException();

boolean free = false;

// 如果c为0,表示当前线程持有的锁都已经释放

if (c == 0) {

free = true;

setExclusiveOwnerThread(null);

}

// 如果c不为0,即可重入锁,锁没有释放完

setState©;

return free;

}

tryRelease 方法是公平锁和非公平锁都公用的,在锁释放的时候,是没有公平和非公平的说法的。从代码中可以看到,锁最终被释放的标椎是 state 的状态为 0,在重入加锁的情况下,需要重入解锁相应的次数后,才能最终把锁释放,比如线程 A 对共享资源 B 重入加锁 5 次,那么释放锁的话,也需要释放 5 次之后,才算真正的释放该共享资源了。

1.2.4 FairSync 公平锁

FairSync 公平锁只实现了 lock 和 tryAcquire 两个方法,如下:

static final class FairSync extends Sync {

// … //

// acquire是AQS的方法:先尝试去获得锁,获取失败后进入同步队列阻塞等待

final void lock() {

acquire(1);

}

// tryAcquire

protected final boolean tryAcquire(int acquires) {

final Thread current = Thread.currentThread();

int c = getState();

if (c == 0) {

// hasQueuedPredecessors()是实现公平锁的关键

// 它会判断当前线程是不是属于同步队列的头节点的下一个节点

// 如果false,可以获得锁

// 如果true,继续等待

if (!hasQueuedPredecessors() &&

compareAndSetState(0, acquires)) {

setExclusiveOwnerThread(current);

return 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;

}

}

1.2.5 NonfairSync 非公平锁

NonfairSync 底层实现了 lock 和 tryAcquire 两个方法,如下:

static final class NonfairSync extends Sync {

// … //

// 加锁

final void lock() {

// CAS 给state赋值

if (compareAndSetState(0, 1))

// CAS赋值成功,表示拿到当前的锁,记录拿到锁的线程

setExclusiveOwnerThread(Thread.currentThread());

else

// acquire 是 AQS 中的方法

// 它会再次尝试获得锁,失败的话进入到同步队列中

acquire(1);

}

protected final boolean tryAcquire(int acquires) {

return nonfairTryAcquire(acquires);

}

}

二、归纳总结

=========================================================================

2.1 lock 加锁


public void lock() {

sync.lock();

}

简单画个图表明代码之间的调用关系:

请添加图片描述

2.2 tryLock 尝试加锁


tryLock 有两个方法,一种是无参的,一种提供了超时时间的入参,两种内部是不同的实现机制,代码分别如下:

// 无参构造器

public boolean tryLock() {

return sync.nonfairTryAcquire(1);

}

// timeout 为超时的时间,在时间内,仍没有得到锁,会返回 false

public boolean tryLock(long timeout, TimeUnit unit)

throws InterruptedException {

return sync.tryAcquireNanos(1, unit.toNanos(timeout));

}

接着我们一起看下两个 tryLock 的调用关系图,下图显示的是无参 tryLock 的调用关系图,如下:

请添加图片描述

注意 :tryLock 无参方法底层走的就是非公平锁实现,没有公平锁的实现。

下图是带有超时时间的有参 tryLock 的调用实现图:

请添加图片描述

2.3 unlock 释放锁


unlock 释放锁的方法,底层调用的是 Sync 同步器的 release 方法release 是 AQS 的方法,分成两步:

  1. 尝试释放锁,如果释放失败,直接返回 false;

  2. 释放成功,从同步队列的头节点的下一个节点开始唤醒,让其去竞争锁。

第一步就是我们上文中 Sync 的 tryRelease 方法,第二步 AQS 已经实现了。

unLock 的源码如下:

// 释放锁

public void unlock() {
sync.release(1);
}

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-gYc4VJEF-1714950250823)]

[外链图片转存中…(img-xPS1fRd8-1714950250823)]

[外链图片转存中…(img-PCPJDB86-1714950250823)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值