当前位置: 首页 > news >正文

浅谈AQS

1.前言

AQS是AbstractQueuedSynchronizer(抽象同步队列)的简写,它是实现同步器的基础组件,并发包下的锁就是通过AQS实现的。作为开发者可能并不会直接用到AQS,但是知道其原理对于架构设计还是很有帮助的。

那为什么说浅谈呢,因为我也仅仅是根据书加上自己的想法来看AQS。

2.LockSupport工具类

在正式讲解AQS之前,我们需要先了解一下LockSupport类,他的主要作用就是用来阻塞唤醒线程。

LockSupport类与每个使用它的线程都会关联一个许可证,在默认情况下调用LockSupport类的方法的线程是不持有许可证的。LockSupport是通过Unsafe类实现的。接下来我们介绍一下LockSupport类的主要方法。

park()

前面我们已经说过,LockSupport类与每个使用它的线程都会关联一个许可证,如果调用park方法的线程持有许可证,那么就会立马返回,否则就会被阻塞挂起。

System.out.println("begin park!");
LockSupport.park();
System.out.println("end park!");

上述代码会在输出begin park之后进入阻塞状态。因为默认情况下是不具有许可证的。

只有在其他线程调用unpark(Thread t)方法并且将该线程作为参数,该线程才能返回。如果其他线程调用了该线程的interrupt()方法,设置了中断标志,或者线程被虚假唤醒(parkNanos方法可以指定阻塞一段时间后自己唤醒,所以可能会出现虚假唤醒!),那么该线程也会返回,所以调用park()方法的时候最好使用循环条件判断方式。

不过使用park阻塞的线程被其他线程中断返回时并不会抛出InterruptedException。

park(Object blocker)

public static void park(Object blocker) {// 获取调用线程Thread t = Thread.currentThread();// Thread类中有一个变量parkBlocker,用来存放park方法传递的blocker对象// 设置该线程的blocker变量setBlocker(t, blocker);// 挂起线程UNSAFE.park(false, 0L);// 线程被激活以后清楚blocker变量setBlocker(t, null);
}

当线程在没有持有许可证调用该方法时,会被阻塞挂起,同时blocker对象会被记录到线程内部。

我们可以使用LockSupport.park(this),这样当程序运行以后,我们使用jstack pid打印线程堆栈可以查看到具体是哪个类被阻塞了。

unpark(Thread thread)

当一个线程调用unpark的时候,如果作为参数的thread没有持有许可证,则会让线程持有。

如果thread之前因为调用park阻塞挂起,则调用unpark后会被唤醒。

如果thread之前还没有调用park,则在调用unpark以后,如果调用park则会立即返回。

3.AQS

初识AQS

请添加图片描述

由类图我们可以知道,AQS是一个FIFO双向队列,通过节点head和tail记录队首队尾元素。

Node

Node节点内部的SHARED用来标记该线程是在获取共享资源时被阻塞挂起放入AQS队列的,EXCLUSIVE用来标识该线程是获取独占资源时被阻塞挂起放入AQS队列的。

Node节点内部有一个成员变量waitStatus记录当前线程等待状态,可以为CANCELLED(线程被取消了)、SIGNAL(线程需要唤醒)、CONDITION(线程在条件队列里等待)、PROPAGATE(释放资源时需要通知其他节点)。

ConditionObject

ConditionObjectNode一样是AQS的内部类。它用来结合锁实现线程同步,其可以访问AQS的内部变量(state和AQS阻塞队列)。

ConditionObject是条件变量,每个条件变量对应一个条件队列,我们可以看到ConditionObject中有两个指针,分别指向条件队列的队尾和队头。条件队列用来存放调用条件变量的await方法后被阻塞的线程。

state

在AQS中,维护了一个单一变量state,对于不同的实现其有不同的意义:

  • 在ReentrantLock中,state表示重入式锁的可重入次数
  • 在ReentrantReadWriteLock中,state的高16位用于表示读锁的可获取次数,低16位用于表示写锁的可重入次数。(state的类型是int,占用4个字节)

Shared与Interruptibly

方法名中带有Shared的方法表示获取或释放共享资源,如acquireShared(int arg)

方法名中带有Interruptibly表示对中断做出响应,当线程调用了带Interruptibly的方法,如果这时被其他线程中断,那么就会抛出InterruptedException返回。

AQS工作流程

独占方式下

获取资源

首先使用tryAcquire尝试获取资源,获取成功直接返回,失败则将当前线程封装为EXCLUSIVE的节点插入到AQS阻塞队列尾部并使用LockSupport.park(this)挂起自己。

public final void acquire(int arg) {// tryAcquire的具体实现要由子类来完成,AQS中并不提供实现if (!tryAcquire(arg) &&// addWaiter的作用是将当前线程封装为独占类型的节点插入AQS阻塞队列,并且将该节点返回// acquireQueuedacquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();
}

释放资源

当一个线程调用 release(int arg) 方法时会尝试使用 tryRelease 操作释放资源,这里是设置状态变量 state 的值,然后调用LockSupport.unpark(thread) 方法激活 AQS 队列里面被阻塞的一个线程 (thread)。被激活的线程则使用tryAcquire尝试,如果条件满足则激活继续向下运行,否则被放入AQS继续阻塞。

public final boolean release(int arg) {if (tryRelease(arg)) {// 释放资源成功则尝试激活线程Node h = head;if (h != null && h.waitStatus != 0)unparkSuccessor(h);return true;}return false;
}

共享方式下

获取资源

线程调用acquireShared(int arg)获取共享资源时,会首先使用tryAcquireShared尝试获取资源(修改state的值),成功直接返回,失败则将当前线程封装为Node.SHARED的Node节点插入到AQS阻塞队列的尾部。

public final void acquireShared(int arg) {if (tryAcquireShared(arg) < 0)doAcquireShared(arg);
}

释放资源

当一个线程调用 releaseShared(int arg) 时会尝试使用 tryReleaseShared 操作释放资 源,这里是设置状态变量 state 的值,然后使用 LockSupport.unpark(thread)激活 AQS 队 列里面被阻塞的一个线程 (thread)。跟独占模式下的流程差不多。

public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;
}

注意

AQS类并没有提供tryAcquire、tryRelease、tryAcquireShare、tryReleaseShare方法,这些方法都需要子类实现。

同时如果你想使用AQS实现一个自己的锁,那你还需要根据情景重写isHeldExclusively方法,用来判断锁是被当前线程共享还是独占。

条件变量

条件变量拥有一个条件队列,条件队列要跟AQS阻塞队列区别开。

当多个线程同时调用lock.lock()方法获取锁时,只有一个线程获取到了锁,其 他线程会被转换为 Node 节点插入到 lock 锁对应的 AQS 阻塞队列里面,并做自旋 CAS 尝试获取锁。
如果获取到锁的线程又调用了对应的条件变量的 await() 方法,则该线程会释放获取 到的锁,并被转换为 Node 节点插入到条件变量对应的条件队列里面。

请添加图片描述

相关文章:

浅谈AQS

1.前言 AQS是AbstractQueuedSynchronizer&#xff08;抽象同步队列&#xff09;的简写&#xff0c;它是实现同步器的基础组件&#xff0c;并发包下的锁就是通过AQS实现的。作为开发者可能并不会直接用到AQS&#xff0c;但是知道其原理对于架构设计还是很有帮助的。 那为什么说…...

关于服务连接器(Servlet)你了解多少?

Servlet 1 简介 Servlet是JavaWeb最为核心的内容&#xff0c;它是Java提供的一门动态web资源开发技术。 使用Servlet就可以实现&#xff0c;根据不同的登录用户在页面上动态显示不同内容。 Servlet是JavaEE规范之一&#xff0c;其实就是一个接口&#xff0c;将来我们需要定义…...

面对学员的投诉,中创教育是如何处理的?

客户满意度的检测指标是客户的期望值和服务感知之间的差距。当顾客购买商品时&#xff0c;对商品本身和企业的服务都抱有良好的愿望和期盼值&#xff0c;如果这些愿望和要求得不到满足&#xff0c;就会失去心理平衡&#xff0c;由此产生的抱怨和想"讨个说法"的行为&a…...

算法问题——排序算法问题

摘要 查找和排序算法是算法的入门知识&#xff0c;其经典思想可以用于很多算法当中。因为其实现代码较短&#xff0c;应用较常见。所以在面试中经常会问到排序算法及其相关的问题。但万变不离其宗&#xff0c;只要熟悉了思想&#xff0c;灵活运用也不是难事。一般在面试中最常…...

ArcGIS网络分析之构建网络分析数据集(一)

说明: 1. 本文主要用于演示网络分析服务的搭建过程。所以在此不会深入讨论网络分析服务的每一个细节,本文的目的就是让初学者学会使用网络分析服务进行基本的分析(主要针对后续的WEB开发):路径分析,最近设施点分析,以及服务区分析。 2.关于OD成本矩阵分析,多路径配送,…...

微电影的行业痛点有哪些?

微电影全称微型电影&#xff0c;又称微影。是指能够通过互联网新媒体平台传播&#xff08;几分钟到60分钟不等&#xff09;的影片&#xff0c;适合在移动状态、短时休闲状态下观看&#xff0c;具有完整故事情节的“微(超短)时”(几分钟-60分钟)放映、“微(超短)周期制作(7-15天…...

spark3.0源码分析-driver-executor心跳机制

前言 driver和executor心跳机制分为两种机制&#xff1a; 1、executor发送心跳机制 2、driver接受心跳机制 至于为何要分为两种&#xff0c;原因是在分布式场景中&#xff0c;服务的稳定性是无法保障的&#xff0c;例如executor宕机后无法发送心跳&#xff0c;故driver端需要…...

数据分析就要选择这款免费报表工具

对于一家企业来说&#xff0c;在日常运营的过程中本身就会产出很多的数据&#xff0c;那么这些数据本身就应该形成报表。可是如果只是选择手工的一种操作&#xff0c;确实需要浪费大量的人力物力。伴随着科技进入到快速发展的阶段&#xff0c;市面上更是出现了很多报表工具可以…...

node学习-3:服务器渲染和客户端渲染

1. 概念 一.服务端渲染&#xff0c;后端嵌套模板&#xff0c;后端渲染模板&#xff0c;SSR&#xff08;后端把页面组装好&#xff09; 做好静态页面&#xff0c;动态效果 把前端代码提供给后端&#xff0c;后端则把静态html以及里面的假数据给删除掉 通过模板进行动态生成h…...

LeetCode刷题笔记和周赛题解总目录

之前一段时间一直在刷LeetCode&#xff0c;在上面积累了很多笔记&#xff0c;这些笔记是做题过程中的一些重要积累和心得&#xff0c;现在将它们汇总和总结至此&#xff0c;此博客将不断更新。 刷题笔记(提供md和pdf两种格式可供下载&#xff0c;不断更新) LeetCode刷题笔记 …...

用类比方式学习编程中函数递归(个人理解仅供参考)(内含汉诺塔问题的求解)

目录 1.前言 2.递归的数学模型 3.相关的c语法 4.将递归的数学模型写成编程语言 5.利用类比方法将实际问题的代码写成函数递归的形式 例1: 例2: 6.汉诺塔问题的求解 1.前言 本人在学习函数递归编程方法的过程中&#xff0c;发现用类比的方式学习递归法可帮助我们在各种编…...

【云原生之Docker实战】使用Docker部署Taskover开源个人任务管理工具

【云原生之Docker实战】使用Docker部署Taskover 开源个人任务管理工具 一、Taskover介绍1.Taskover 简介2.Taskover功能二、检查本地docker环境1.检查系统版本2.检查docker版本3.检查docker状态4.检查docker compose版本三、下载Taskover镜像四、部署Taskover应用1.创建安装目录…...

5、SQL编程开发与注意事项

1.1 导入数据 导入测试库: 文档地址: https://dev.mysql.com/doc/employee/en/sakila-structure.html下载地址: https://github.com/datacharmer/test_db导入测试库: mysql -uroot -p -S < employees.sql 1.2 库操作 增:create database test character set utf8;删:d…...

Allegro如何通过视图显示区分动态和静态铜皮操作指导

Allegro如何通过视图显示区分动态和静态铜皮操作指导 用Allegro做PCB设计的时候,通常动态和静态铜皮是无法通过视图显示区分的,只能通过show element查看得知,如下图 左边铜皮是动态铜皮,右边是静态铜皮 但Allegro可以通过一些设置让动静态铜皮以不同效果显示出来 具体操…...

测试开发之Django实战示例 第十一章 渲染和缓存课程内容

第十一章 渲染和缓存课程内容在上一章中&#xff0c;使用了模型继承和通用关系建立弹性的课程、章节和内容的关联数据模型&#xff0c;并且建立了一个CMS系统&#xff0c;在其中使用了CBV&#xff0c;表单集和AJAX管理课程内容。在这一章将要做的事情是&#xff1a;创建公开对外…...

90%企业在探索的敏捷开发怎么做?极狐GitLab总结了这些逻辑与流程

本文来自&#xff1a; 彭亮 极狐(GitLab) 高级产品经理 毛超 极狐(GitLab) 研发工程师 极狐(GitLab) 市场部内容团队 “敏捷” 是指能够驾驭变化&#xff0c;保持组织竞争优势的一种能力。自 2001 年《敏捷宣言》以来&#xff0c;敏捷及敏捷开发理念逐渐席卷全球。中国信通院《…...

LeetCode-257. 二叉树的所有路径

目录题目分析递归法题目来源 257. 二叉树的所有路径 题目分析 前序遍历以及回溯的过程如图&#xff1a; 递归法 1.递归函数参数以及返回值 要传入根节点&#xff0c;记录每一条路径的path&#xff0c;和存放结果集的result&#xff0c;这里递归不需要返回值&#xff0c;代…...

测试用例该怎么设计?—— 日常加更篇(下)

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…...

Java基础:接口

1.接口的概念 当不是所有子类, 而是多个子类都包含一个方法时, 为了代码书写规范性, 可以用自定义的接口来统一该方法的书写规范. 所以接口可以看作是一种书写规则. 接口是对行为的抽象 抽象类一般是书写在父类当中, 接口是单独书写的, 不是一种类 2.接口的定义和使用 3.接口…...

vuex基础入门:uniapp实现用户登录授权实战

1.背景 vuex是数据共享方案之一,本文以微信小程序登录授权为例介绍一下vuex常用属性state、getters、mutations、actions. 2.基于uniapp实现微信小程序登录授权流程 1.凡是需要用户登录授权信息的页面创建时created方法中需要判断用户是否登录,需要使用本地缓存的token调用服务…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

基于大模型的 UI 自动化系统

基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

设计模式和设计原则回顾

设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

02.运算符

目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&&#xff1a;逻辑与 ||&#xff1a;逻辑或 &#xff01;&#xff1a;逻辑非 短路求值 位运算符 按位与&&#xff1a; 按位或 | 按位取反~ …...