深入理解Java并发类——AQS
什么是AQS
AbstractQueuedSynchronizer,是Java并发包中locks目录下的一个抽象类,它是实现各种同步结构和部分其他组成单元的基础,如线程池中的Worker。
为什么需要AQS
Doug Lea曾经介绍过AQS的设计初衷。从原理上,一种同步结构往往是可以利用其他的结构实现的。但是,对某种同步结构的倾向,会导致复杂的实现逻辑,所以他选择将基础的同步相关操作抽象在AQS中,利用AQS为我们构建同步结构提供范本。
抽象类的特点看这里
AQS的核心思想
如果被请求的共享资源空闲,则将请求资源的线程设为有效的工作线程,并将资源锁定,资源使用结束时释放锁定。如果资源被锁定,则把请求未果的线程加入FIFO队列,当资源可用时唤醒队头的线程。
这个过程中,通过state判断资源是否锁定,并通过对state进行CAS操作,来蟹盖资源的锁定状态。
AQS的内部数据和方法
一个volatile的整数成员表征状态,同时提供了setState、getState、CAS方法。
private volatile int state; //当state的值与期望值相同时,把state设为update
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
一个先入先出(FIFO)的等待线程队列,以实现多线程间竞争和等待,这是AQS机制的核心之一。
各种基于CAS的基础操作方法,以及各种期望具体同步结构去实现的acquire/release方法。
如何利用AQS实现同步结构
利用AQS实现一个同步结构,至少要实现两个基本类型的方法,分别是:
- acquire操作,获取资源的独占权.
- release操作,释放对某个资源的独占。
AQS使用了模板设计模式,自定义同步容器要重写这些模板方法。
//独占方式
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
//共享方式
protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}
protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}
//该线程是否正在独占资源,只有用到condition时才重写它
protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}
ReentrantLock对AQS的利用
以ReentrantLock为例,它内部通过继承AQS抽象类实现了Sync子类,以AQS的state来表示和操作锁的状态。
private final Sync sync;
absract static class Sync extends AbsractQueuedSynchronizer {
abstract void lock();
final boolean nonfairTryAcquire(int acquires) {...}
protected final boolean tryRelease(int releases) {...}
...
}
下面是ReentrantLock对应acquire和release操作,如果是CountDownLatch则可以看作是await()/countDown(),具体实现也有区别。
初始化时staate=0,表示资源未锁定,线程A调用lock()后,state+1,此后其他线程再lock()就会失败,直到A进行unlock(),state-1。
另外,A可以重复获取此锁,每次获取state累加,获取多少次就释放多少次,这就是可重入。
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}
尝试获取锁
下面是线程试图获取锁的过程,acquire()的实现逻辑是在AQS的内部,调用了tryAcquire和acquireQueued。
tryAcquire是按照特定场景需要开发者去实现的部分,而线程间竞争则是AQS通过Waiter队列与acquireQueued提供的。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
在ReentrantLock中,tryAcquire的逻辑实现在NonfairSync和FairSync中,分别提供了进一步的非公平或公平性方法,而AQS内部tryAcquire仅仅是个接近未实现的方法(直接抛异常)。
ReentrantLock默认是非公平的,下面是公平性在ReentrantLock构建时如何设定的。
public ReentrantLock() {
sync = new NonfairSync(); // 默认是非公平的
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
以非公平的tryAcquire为例,其内部实现了如何配合状态与CAS获取锁。对比公平版本的tryAcquire,非公平版本在锁无人占有时,直接抢占,并不检查是否有其他等待者,这里体现了非公平语义。
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()) { //虽然状态不是0,但当前线程是锁持有者,则进行重入
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
获取锁失败,排队竞争
接下来分析acquireQueued,如果前面的tryAcquire失败,代表着锁争抢失败,进入排队竞争阶段。
当前线程会被包装成为一个排他模式的节点(EXCLUSIVE),通过addWaiter方法添加到FIFO队列中。
如果当前节点的前面是头节点,则试图获取锁,一切顺利则成为新的头节点;否则,有必要则等待。
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {// 循环
final Node p = node.predecessor();// 获取前一个节点
if (p == head && tryAcquire(arg)) { // 如果前一个节点是头结点,表示当前节点合适去tryAcquire
setHead(node); // acquire成功,则设置新的头节点
p.next = null; // 将前面节点对当前节点的引用清空
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node)) // 检查是否失败后需要park
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);// 出现异常,取消
if (interrupted)
selfInterrupt();
throw t;
}
}
总结。以上是对ReentrantLock的acquire方法的分析,release方法与之相似。
参考
《Java并发编程实战》
《Java核心技术36讲》杨晓峰
深入理解Java并发类——AQS的更多相关文章
- 深入理解Java并发框架AQS系列(一):线程
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.概述 1.1.前言 重剑无锋,大巧不工 读j.u.c包下的源码,永远无法绕开的经典 ...
- 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 一.AQS框架简介 AQS诞生于Jdk1.5,在当时低效且功能单一的synchroni ...
- 深入理解Java并发框架AQS系列(四):共享锁(Shared Lock)
深入理解Java并发框架AQS系列(一):线程 深入理解Java并发框架AQS系列(二):AQS框架简介及锁概念 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock) 深入 ...
- 深入理解Java并发框架AQS系列(三):独占锁(Exclusive Lock)
一.前言 优秀的源码就在那里 经过了前面两章的铺垫,终于要切入正题了,本章也是整个AQS的核心之一 从本章开始,我们要精读AQS源码,在欣赏它的同时也要学会质疑它.当然本文不会带着大家逐行过源码(会有 ...
- JAVA并发-同步器AQS
什么是AQS aqs全称为AbstractQueuedSynchronizer,它提供了一个FIFO队列,可以看成是一个用来实现同步锁以及其他涉及到同步功能的核心组件,常见的有:ReentrantLo ...
- 魔鬼在细节,理解Java并发底层之AQS实现
jdk的JUC包(java.util.concurrent)提供大量Java并发工具提供使用,基本由Doug Lea编写,很多地方值得学习和借鉴,是进阶升级必经之路 本文从JUC包中常用的对象锁.并发 ...
- 【转】Java并发的AQS原理详解
申明:此篇文章转载自:https://juejin.im/post/5c11d6376fb9a049e82b6253写的真的很棒,感谢老钱的分享. 打通 Java 任督二脉 —— 并发数据结构的基石 ...
- 深入理解 Java 并发锁
本文以及示例源码已归档在 javacore 一.并发锁简介 确保线程安全最常见的做法是利用锁机制(Lock.sychronized)来对共享数据做互斥同步,这样在同一个时刻,只有一个线程可以执行某个方 ...
- JAVA并发(1)-AQS(亿点细节)
AQS(AbstractQueuedSynchronizer), 可以说的夸张点,并发包中的几乎所有类都是基于AQS的. 一起揭开AQS的面纱 1. 介绍 为依赖 FIFO阻塞队列 的阻塞锁和相关同步 ...
随机推荐
- 利用js判断文件是否为utf-8编码
常规方案 使用FileReader以utf-8格式读取文件,根据文件内容是否包含乱码字符�,来判断文件是否为utf-8. 如果存在�,即文件编码非utf-8,反之为utf-8. 代码如下: const ...
- NNVM AI框架编译器
NNVM AI框架编译器 深度学习已变得无处不在且不可或缺.看到对在多种平台(例如手机,GPU,IoT设备和专用加速器)上部署深度学习工作负载的需求不断增长.TVM堆栈弥合深度学习框架与面向性能或效率 ...
- 从PyTorch到ONNX的端到端AlexNet
从PyTorch到ONNX的端到端AlexNet 这是一个简单的脚本,可将Torchvision中定义的经过预训练的AlexNet导出到ONNX中.运行一轮推理Inference,然后将生成的跟踪模型 ...
- java面试必知必会——排序
二.排序 时间复杂度分析 排序算法 平均时间复杂度 最好 最坏 空间复杂度 稳定性 冒泡 O(n²) O(n) O(n²) O(1) 稳定 选择 O(n²) O(n²) O(n²) O(1) 不稳定 ...
- maven 安装、下载、配置,idea中的maven设置
1.从Maven官网下载压缩包 2.将压缩包解压到你像放置Maven的路径,我放置在D:\0_FileSave\Maven 3.创建本地仓库 MavenRepository 在Maven解压路径下D ...
- ubuntu 如何更改 grub 界面主题
ubuntu 如何更改 grub 界面主题 安装 Liunx 系统的人都知道,系统引导是通过 grub 去引导的,但是 grub 这个界面就很单调,大概是这样子的 这肯定不符合我们潮流青年的审美的~ ...
- 【VBA】返回指定范围内的随机整数
返回指定范围内的随机整数: Sub main() Randomize Debug.Print 随机整数(1, 2) End Sub Function 随机整数(a As Integer, b As I ...
- 使用 Docker 部署 Node 应用 - 镜像文件尺寸的优化
前面 使用 Docker 部署 Node 应用 一文中完成了镜像的创建和运行,不过生成的镜像还有些粗糙,需要进一步优化. 镜像的优化 通过 docker images 看到简单的一个 node 服务端 ...
- Java基础面试题(史上最全、持续更新、吐血推荐)
文章很长,建议收藏起来,慢慢读! 疯狂创客圈为小伙伴奉上以下珍贵的学习资源: 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 ...
- vagrant+java+springcloud+redis+zookeeper镜像下载(&制作详解)
文章很长,建议收藏起来,慢慢读! 备注:持续更新中..... 疯狂创客圈 经典图书 : <Netty Zookeeper Redis 高并发实战> 面试必备 + 大厂必备 + 涨薪必备 疯 ...