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;
}
-
通过判断 AQS 的 state 的状态来决定是否可以获得锁,0 表示锁是空闲的;
-
else if 的代码体现了可重入加锁,同一个线程对共享资源重入加锁,底层实现就是把 state + 1,并且可重入的次数是有限制的,为 Integer 的最大值;
-
这个方法是非公平的,所以只有非公平锁才会用到,公平锁是另外的实现。
无参的 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);
}
}
=========================================================================
public void lock() {
sync.lock();
}
简单画个图表明代码之间的调用关系:
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 的调用实现图:
unlock 释放锁的方法,底层调用的是 Sync 同步器的 release 方法,release 是 AQS 的方法,分成两步:
-
尝试释放锁,如果释放失败,直接返回 false;
-
释放成功,从同步队列的头节点的下一个节点开始唤醒,让其去竞争锁。
第一步就是我们上文中 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开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!