java多线程(七)AQS(AbstractQueuedSynchronizer)技术解析:以赛跑起跑场景为例

AQS概括

核心思想

AQS(AbstractQueuedSynchronizer)是Java并发包中的一个核心同步器框架,它定义了一套多线程访问共享资源的同步机制。

其核心思想是:利用一个volatile的int类型的变量state来表示同步状态,并通过一个FIFO(先进先出)队列来管理获取同步状态失败的线程。

当线程无法获取同步状态时,会被放入等待队列中阻塞,直到同步状态被释放,队列中的线程被唤醒并重新尝试获取同步状态。

数据结构

AQS中的等待队列是一个基于双向链表的FIFO队列。

队列中的每个节点(Node)代表一个等待获取同步状态的线程,节点之间通过prevnext指针相互连接。

此外,每个节点还包含线程引用(thread)、等待状态(waitStatus)等信息。

工作原理

AQS使用一个int类型的成员变量state来表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。

线程通过CAS(Compare-And-Swap)操作来修改AQS的同步状态。

如果线程获取同步状态失败(例如state不为0),AQS则会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会阻塞当前线程。

当同步状态释放时,则会把节点中的线程唤醒,使其再次尝试获取同步状态。

资源共享方式

AQS支持两种资源共享方式:

  1. 独占模式(Exclusive Mode):在这种模式下,一次只有一个线程能够获取到同步状态。例如,ReentrantLock就是以独占模式实现的。

  2. 共享模式(Shared Mode):在这种模式下,允许多个线程同时获取到同步状态,但是每次获取到的资源量可能不同。例如,SemaphoreCountDownLatch就是以共享模式实现的。

重要方法

AQS提供了一系列重要方法用于实现同步状态的管理和线程的阻塞与唤醒,包括:

  • acquire(int arg): 以独占模式获取同步状态,如果获取失败则进入等待队列。

  • release(int arg): 释放同步状态,并唤醒等待队列中的一个或多个线程。

  • tryAcquire(int arg): 尝试以独占模式获取同步状态,成功则返回true,失败则返回false。

  • tryRelease(int arg): 尝试释放同步状态,成功则返回true,失败则返回false。

  • tryAcquireShared(int arg): 尝试以共享模式获取同步状态。

  • tryReleaseShared(int arg): 尝试以共享模式释放同步状态。

应用场景

AQS广泛应用于Java并发包中的各种同步组件中,如ReentrantLockSemaphoreCountDownLatch等。

它为这些同步组件提供了一个统一的框架和机制来实现多线程的同步和协调。

赛跑起跑场景中的AQS应用

CountDownLatch是Java并发包中的一个同步工具,它允许一个或多个线程等待其他线程完成一组操作。

CountDownLatch是基于AQS框架实现的,它继承了AQS并重写了tryAcquireShared方法来尝试获取同步状态。

CountDownLatch中,state的初始值被设置为构造方法中传入的计数值,表示需要等待的线程数量。

当调用await方法时,如果state不为0,当前线程会进入等待状态,并将其封装成Node节点加入AQS的等待队列。

当调用countDown方法时,state的值会递减。如果state减至0,AQS会唤醒等待队列中的所有线程。

赛跑起跑场景中的AQS应用

假设有一个田径比赛,有8名选手参赛。为了保证比赛的公平性,我们需要确保所有选手在听到枪声后同时起跑。

这里,我们可以使用CountDownLatch来实现这一功能,而CountDownLatch则是基于AQS实现的。

以下是赛跑起跑场景的示例代码:

package com.hmblogs.backend.study.thread;import java.util.concurrent.CountDownLatch;public class RaceStartWithCountDownLatch {public static void main(String[] args) throws InterruptedException {int numberOfRunners = 8; // 参赛选手数量CountDownLatch startSignal = new CountDownLatch(numberOfRunners); // 初始化起跑信号for (int i = 0; i < numberOfRunners; i++) {new Thread(new Runner(startSignal, "Runner " + (i + 1))).start();}// 这里为了示例的简洁性,只调用了一次countDown,实际使用时需要确保调用次数与选手数量一致// 正确的做法是在所有选手都准备好后,再一次性调用numberOfRunners次countDownSystem.out.println("Starting the race...");for (int i = 0; i < numberOfRunners; i++) {//如果不加该for逻辑则不会有选手起跑。startSignal.countDown(); // 发出起跑信号,让所有选手起跑(实际应调用多次)}}static class Runner implements Runnable {private final CountDownLatch startSignal;private final String name;Runner(CountDownLatch startSignal, String name) {this.startSignal = startSignal;this.name = name;}@Overridepublic void run() {try {System.out.println(name + " is ready and waiting for the start signal...");startSignal.await(); // 等待起跑信号System.out.println(name + " has started running!");} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}

在上面的代码中,我们创建了一个CountDownLatch实例startSignal,其计数器的初始值为参赛选手的数量。

每个选手线程在调用startSignal.await时,都会尝试获取同步状态。由于state不为0,它们会进入等待状态。

当主线程(或起跑控制线程)调用startSignal.countDown时,state的值会递减。但是,由于示例代码中只调用了一次countDown,所以state不会减至0,选手线程不会立即被唤醒。

为了修正这一点,应该确保在发出起跑信号时调用足够次数的countDown,使得state减至0,从而唤醒所有等待的选手线程。

最后

AQS作为一个通用的同步框架,为Java并发编程提供了极大的便利。

通过继承AQS并重写相应的方法,开发者可以轻松地实现自定义的同步逻辑,而无需深入了解底层的同步机制。

这使得Java并发编程变得更加简单、高效。

此外,AQS还提供了丰富的同步特性,如可重入性、可中断性、超时等,这些特性使得基于AQS实现的同步工具更加灵活、强大。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/406742.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ansible:

ansible&#xff1a; 远程自动化运维 ansible是基于python开发的配置管理和应用部署工具。 也是自动化运维的重要工具。 可以批量配置&#xff0c;部署&#xff0c;管理上千台主机。 只需要在一台主机配置ansible就可以完成其他主机的操作。 操纵模式&#xff1a; 1、模…

黑神话:悟空-配置推荐

显卡推荐&#xff08;按类别整理&#xff09; 1. GTX 10系列、GTX 16系列&#xff1a; 如果希望体验光线追踪&#xff0c;建议根据预算升级到RTX 40系列显卡。对于1080p分辨率&#xff0c;至少需要RTX 4060才能流畅运行。 2. RTX 20系列&#xff1a; RTX 2060、RTX 2070&#…

第二十七节、人物可互动标识

一、多个场景同时存在 方法&#xff1a;将另一个场景拖拽进 当前场景中 这样在一个场景中保存物体&#xff0c;另一个场景切换即可 创建一个场景名为上图&#xff08;这是一个持久化的场景&#xff09; 被激活的场景是粗体字的 二、代码 使用第二个代码获得player的子物体 …

SQL注入(cookie、base64、dnslog外带、搜索型注入)

目录 COOKIE注入 BASE64注入 DNSLOG注入—注入判断 什么是泛解析&#xff1f; UNC路径 网上邻居 LOAD_FILE函数 搜索型注入—注入判断 本文所使用的sql注入靶场为sqli-labs-master&#xff0c;靶场资源文件已上传&#xff0c;如有需要请前往主页或以下链接下载 信安必备…

AVI-Talking——能通过语音生成很自然的 3D 说话面孔

概述 论文地址&#xff1a;https://arxiv.org/pdf/2402.16124v1.pdf 逼真的人脸三维动画在娱乐业中至关重要&#xff0c;包括数字人物动画、电影视觉配音和虚拟化身的创建。以往的研究曾试图建立动态头部姿势与音频节奏之间的关联模型&#xff0c;或使用情感标签或视频剪辑作…

SpringBoot Web请求、响应

一、文章概述 请求方面主要讲&#xff0c;当前端向后端发出请求时&#xff0c;对于不同类型的参数后端都如何接收&#xff1b;对于响应&#xff0c;文章会讲解后端如何向前端响应数据以及如何使返回的数据具有统一的格式。 二、请求 2.1接收简单参数 Controller方法&#xf…

算法的学习笔记—二叉搜索树的后序遍历序列(牛客JZ33)

&#x1f600;前言 在数据结构与算法的学习中&#xff0c;二叉搜索树&#xff08;BST&#xff09;是一个重要的概念&#xff0c;而后序遍历则是树的遍历方式之一。今天&#xff0c;我们将深入探讨一个经典问题&#xff1a;如何判断一个给定的整数数组是否是某个二叉搜索树的后序…

【Prettier】代码格式化工具Prettier的使用和配置介绍

前言 前段时间&#xff0c;因为项目的prettier的配置和eslint格式检查有些冲突&#xff0c;在其prettier官网和百度了一些配置相关的资料&#xff0c;在此做一些总结&#xff0c;以备不时之需。 Prettier官网 Prettier Prettier 是一种前端代码格式化工具&#xff0c;支持ja…

甘肃旅游服务平台代码--论文pf

TOC springboot422甘肃旅游服务平台代码--论文pf 绪论 1.1 研究背景 现在大家正处于互联网加的时代&#xff0c;这个时代它就是一个信息内容无比丰富&#xff0c;信息处理与管理变得越加高效的网络化的时代&#xff0c;这个时代让大家的生活不仅变得更加地便利化&#xff0…

Day45 | 99.岛屿数量 深搜 广搜 100.岛屿的最大面积

语言 Java 99.岛屿数量 深搜 广搜 99. 岛屿数量 题目 题目描述 给定一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的矩阵&#xff0c;你需要计算岛屿的数量。岛屿由水平方向或垂直方向上相邻的陆地连接而成&#xff0c;并且四周都是水域。你可…

【启明智显技术分享】实时操作系统RTOS核心机制与应用

在当今这个对实时性要求日益严苛的嵌入式系统时代&#xff0c;RTOS作为核心软件架构&#xff0c;正扮演着不可或缺的角色。而当我们深入探讨RTOS的广泛应用与优势时&#xff0c;不得不提到启明智显Model系列芯片以其卓越的性能、丰富的外设接口以及对RTOS系统的全面支持&#x…

Qt实现圆型控件的三种方法之子类化控件并重写paintEvent

前言 最近在研究绘制各种形状的控件&#xff0c;这里专门挑出圆形的控件进行记录&#xff0c;其它形状的也大差不差&#xff0c;会了圆形的之后其它的也类似。 正文 这里我挑出Label来进行举例。 子类化 QLabel 并重写 paintEvent 如果需要更复杂的自定义绘制&#xff0c;…

【CSS】使用 CSS 自定义属性(变量)-- var()

自定义属性&#xff08;有时候也被称作CSS 变量或者级联变量&#xff09;是由 CSS 作者定义的&#xff0c;它包含的值可以在整个文档中重复使用。由自定义属性标记设定值&#xff08;比如&#xff1a; --main-color: black;&#xff09;&#xff0c;由 var() 函数来获取值&…

算法全面剖析

算法 查找算法&#xff1a; 顺序查找&#xff1a; 基本思想&#xff1a; 顺序查找也称为线形查找&#xff0c;属于无序查找算法。从数据结构线形表的一端开始&#xff0c;顺序扫描&#xff0c;依次将扫描到的结点关键字与给定值k相比较&#xff0c;若相等则表示查找成功&am…

Nginx--监控

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、Nginx的基础监控 进程监控 端口监控 注意&#xff1a; 这两个是必须要加在zabbix监控&#xff0c;加触发器有问题及时告警。 nginx 提供了ngx…

编译linux内核时,让版本号不跟着git变化

文章目录 编译linux内核时&#xff0c;让版本号不跟着git变化现象方法一方法二 编译linux内核时&#xff0c;让版本号不跟着git变化 现象 内核每次重新编译时&#xff0c;uname -r都会跟着变。 4.1.15-00005-g482731e4-dirty 导致报错&#xff0c;modprobe: can’t change …

前端算法 | LeetCode第 70 题爬楼梯问题

目录 流程分析 归纳法分析 为什么是斐波那契数列&#xff1f; 推导过程&#xff1a; 解法1&#xff1a;循环累加计算 解法2&#xff1a;递归计算 解法3&#xff1a;利用数组特性 解法4&#xff1a;利用 JavaScript ES6 新特性 拓展知识&#xff1a;每次可以走 1 步、2…

ClickHouse实时探索与实践 京东云

1 前言 京喜达技术部在社区团购场景下采用JDQFlinkElasticsearch架构来打造实时数据报表。随着业务的发展 Elasticsearch开始暴露出一些弊端&#xff0c;不适合大批量的数据查询&#xff0c;高频次深度分页导出导致ES宕机、不能精确去重统计&#xff0c;多个字段聚合计算时性能…

位运算专题

分享丨【题单】位运算&#xff08;基础/性质/拆位/试填/恒等式/思维&#xff09; - 力扣&#xff08;LeetCode&#xff09; Leetcode 3133. 数组最后一个元素的最小值 我的答案与思路&#xff1a; class Solution { public: // 4 --> (100)2 7 --> (0111)2 // 5 --&g…

怎么让FLV转MP4?建议试试这样做

怎么让FLV转MP4&#xff1f;在数字视频处理的日常中&#xff0c;我们经常会遇到不同格式的视频文件需要相互转换的情况。FLV&#xff08;Flash Video&#xff09;作为一种早期的网络视频格式&#xff0c;虽然在互联网上仍有一定应用&#xff0c;但对比来说&#xff0c;MP4格式更…