聊聊ReentrantLock基于AQS的公平锁和非公平锁的实现区别
ReentrantLock锁的实现是基于AQS实现的,所以先简单说下AQS:
AQS是AbstractQueuedSynchronizer缩写,顾名思义:抽象的队列同步器,它是JUC里面许多同步工具类实现的核心
其实简单来说AQS有两个核心,一个是volatile修饰的int类型state,这个是记录处于等待中需要持有锁和正在持有锁的线程数量
/**
* The synchronization state.
*/
private volatile int state;
第二个就是Node内部类,他是AQS里面FIFO双向队列节点的实现,他的一些属性如下:
static final class Node {
volatile int waitStatus;//等待状态
volatile Node prev;//前驱节点
volatile Node next;//后继节点
volatile Thread thread;//申请同步状态的线程
Node nextWaiter;//等待节点的后继节点(后续出AQS详细篇再细讲)
}
这种结构可以从任意的一个节点开始很方便的访问前驱和后继节点。每个Node由线程封装,当线程竞争失败后会加入到AQS队列中去。
基于这些再继续聊下ReentrantLock的公平和非公平锁的实现
ReentrantLock锁的实现基于AQS,如下sync抽象内部类:
abstract static class Sync extends AbstractQueuedSynchronizer{
abstract void lock();
}
Sync的子类有两个:FairSync和NonfairSync(公平和非公平锁的具体实现类),两个锁的不同就在于两个方法的实现不同lock():加锁的方法,还有tryAcquire(int acquires):尝试持有锁的方法,获取成功返回true,失败返回false
看看两把锁的源码实现:
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
nonfairTryAcquire方法是在父类Sync中定义:
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;
}
可以看见,在使用lock()加锁的时候就已经体现了非公平性了,因为lock()加锁的时候直接尝试使用CAS获取锁,如果获取到了就不会入等待队列,所以会有后来的线程先抢占到锁;
那如果没有抢占到锁呢?会走else的acquire(1)方法。
先看看acquire方法的定义:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
这里会发现acquire调用了tryAcquire方法,而在上面两把锁的源码中各自重写了tryAcquire方法
这个方法中当getState()获取同步状态为0(没有线程持有锁)时候,继续使用用CAS获取锁
两个重写的方法唯一不同之处在于,公平锁加了hasQueuedPredecessors()方法:
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
这个方法主要是用来判断线程需不需要排队,因为队列是FIFO的,所以需要判断队列中有没有相关线程的节点已经在排队了。有则返回true表示线程需要排队,没有则返回false则表示线程无需排队。
而非公平锁就没有判断FIFO队列是否还有排队。
小结:
ReentrantLock中公平锁和非公平锁的实现主要有两处区别:
1.在lock()方法中,非公平锁一进来就尝试CAS获取锁,不会管等待队列里面是否有等待线程
2.在tryAcquire方法中,判断state等于0(没有线程持有锁的情况)后,公平锁会先调用hasQueuedPredecessors()方法判断FIFO队列是否有等待线程,没有才继续尝试获取锁,而非公平锁是直接CAS获取锁
聊聊ReentrantLock基于AQS的公平锁和非公平锁的实现区别的更多相关文章
- Java并发指南8:AQS中的公平锁与非公平锁,Condtion
一行一行源码分析清楚 AbstractQueuedSynchronizer (二) 转自https://www.javadoop.com/post/AbstractQueuedSynchronizer ...
- 深入了解ReentrantLock中的公平锁和非公平锁的加锁机制
ReentrantLock和synchronized一样都是实现线程同步,但是像比synchronized它更加灵活.强大.增加了轮询.超时.中断等高级功能,可以更加精细化的控制线程同步,它是基于AQ ...
- 理解ReentrantLock的公平锁和非公平锁
学习AQS的时候,了解到AQS依赖于内部的FIFO同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个Node对象并将其加入到同步队列,同时会阻塞当 ...
- 死磕 java同步系列之ReentrantLock源码解析(一)——公平锁、非公平锁
问题 (1)重入锁是什么? (2)ReentrantLock如何实现重入锁? (3)ReentrantLock为什么默认是非公平模式? (4)ReentrantLock除了可重入还有哪些特性? 简介 ...
- ReentrantLock中的公平锁与非公平锁
简介 ReentrantLock是一种可重入锁,可以等同于synchronized的使用,但是比synchronized更加的强大.灵活. 一个可重入的排他锁,它具有与使用 synchronized ...
- 深入分析ReentrantLock公平锁和非公平锁的区别
在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQ ...
- java多线程20 : ReentrantLock中的方法 ,公平锁和非公平锁
公平锁与非公平锁 ReentrantLock有一个很大的特点,就是可以指定锁是公平锁还是非公平锁,公平锁表示线程获取锁的顺序是按照线程排队的顺序来分配的,而非公平锁就是一种获取锁的抢占机制,是随机获得 ...
- Java之ReentrantLock公平锁和非公平锁
在Java的ReentrantLock构造函数中提供了两种锁:创建公平锁和非公平锁(默认).代码如下: public ReentrantLock() { sync = new NonfairSync( ...
- 深入分析ReentrantLock公平锁和非公平锁的区别 (转)
在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQ ...
随机推荐
- GO语言安装以及国内镜像
首先,下载GO语言,国内的话用 Go下载 - Go语言中文网 - Golang中文社区 (studygolang.com) 可能会快一点 然后根据自己的系统选择下载的包,我是win10,就选go1.1 ...
- Linux、Windows 下手动生成 sha256 等类型的校验文件
目录 1 - 校验文件的作用 2 - Linux 下生成校验文件 3 - Windows 下生成校验文件 参考资料 版权声明 1 - 校验文件的作用 从网服务器下载文件,尤其是比较大的文件时,很容易由 ...
- 01-ADB命令获取包名、界面名
命令代码 adb shell dumpsys window |findstr mFocusedApp
- SQL 练习12
查询和" 01 "号的同学学习的课程 完全相同的其他同学的信息 分析 如果某同学学的某一个课程和01同学所学的课程有对应,那么子查询返回false. 如果没有对应,子查询返回tru ...
- Maven在IDEA中的日常使用
1.为什么使用MavenMaven是我们在开发过程中常用的工具,主要用途有两种:1)方便的下载jar包2)项目打包接下来以windows操作系统为例,介绍一下Maven在IDEA中如何设置和常用的功能 ...
- Linux添加防火墙、iptables的安装和配置
由于centos7默认是使用firewall作为防火墙,下面介绍如何将系统的防火墙设置为iptables. #停止firewall systemctl stop firewall.service #禁 ...
- lwm2m 协议
1.DTLS介绍 1.1 DTLS的作用 互联网先驱们最开始在设计互联网协议时主要考虑的是可用性,安全性是没有考虑在其中的,所以传输层的TCP.UDP协议本身都不具备安全性.SSL/TLS协议是基于T ...
- .Net Core WebApi (一) --Config
ASP.NET Core launchsettings.json 文件 位置:项目根文件夹的"Properties"文件夹中 使用:从 Visual Studio 或使用.NET ...
- mysql复制内容到一张新表
-- 1.复制表结构及数据到新表 CREATE TABLE 新表 SELECT * FROM 旧表 -- 2.只复制表结构到新表 CREATE TABLE 新表 SELECT * FROM 旧表 WH ...
- Go并发控制--WaitGroup篇
目录 1. 前言 2. 使用WaitGroup控制 2.1 使用场景 2.2 信号量 1.3 WaitGroup 数据结构 2.3.1 Add () 方法 2.3.2 Wait() 2.3.3 Don ...