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

news2024/11/25 12:44:29

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次countDown
        System.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;
        }

        @Override
        public 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.coloradmin.cn/o/2063568.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信自动回复,周末也能轻松应对!

相信很多人都有过这样的经历&#xff1a;休息的时候&#xff0c;手机响个不停&#xff0c;生怕漏掉一个客户消息&#xff0c;结果一不小心就让客户流失了&#xff01; 要想解决这个问题&#xff0c;你只需一个多微管理系统&#xff0c;让我们一起来看看它的自动回复设置吧&…

这个方法完美解决我的Jenkins插件不能下载安装的问题

1、打开这个地址&#xff08;前提是jenkins是开启的哦&#xff09;http://localhost:8080/pluginManager/advanced 。 2、在最下面update site 改成http://updates.jenkins.io/update-center.json 。 3、服务列表中关闭jenkins&#xff0c;再重新启动&#xff0c;就能联网下载了…

叉车AI行车防撞监控系统方案,二级报警区域,守护人与车的安全!

九盾叉车AI行车防撞监控系统安装在叉车驾驶室顶的前后单独安装ADAS摄像头&#xff0c;结合深度学习算法以完成机器视觉的识别工作&#xff0c;分别安装在车辆护顶架前后方&#xff0c;进行180二级区域视频监控&#xff0c;同时解决二个方向维度的视野盲区&#xff0c;可根据距离…

数据结构(6.2_4)——图的基本操作

注&#xff1a;只探讨邻接矩阵和邻接表怎么实现图的基本操作 Adjacent(G,x,y):判断图G是否存在边<x,y>或(x,y) 领接矩阵 邻接表 有向图&#xff1a; Neighbors(G,x):列出图G中与结点x邻接的边 有向图 InsertVertex(G,x):在图G中插入顶点x DeleteVertex(G,x):在图G中删除…

【蓝桥杯集训100题】scratch时间计算 蓝桥杯scratch比赛专项预测编程题 集训模拟练习题第26题

目录 scratch时间计算 一、题目要求 编程实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、python资料 scratc…

125-隧道技术SMBICMP正反向连接防火墙出入规则上线

参考&#xff1a;【内网安全】 隧道技术&SMB&ICMP&正反向连接&防火墙出入规则上线_第125天:内网安全-隧道技术&smb&icmp&正反向连接&防火墙出入规则上线-CSDN博客 怎么知道对方是出站限制还是入站限制呢&#xff1f; 上传正向和反向木马进行测…

面试准备算法

用最少数量的箭引爆气球 class Solution { public:class MyCompare{public:bool operator()(vector<int>& a, vector<int>& b){return a[0] < b[0];}};int findMinArrowShots(vector<vector<int>>& points) {int count 1;MyCompare c…

啥是粘包和半包,咋解决?

写在前面 本文看下半包和粘包。 1&#xff1a;什么是半包和粘包&#xff0c;以及如何解决 因为网络传输数据都是一个数据包一个数据包传输的&#xff0c;就像这样&#xff1a; 在读取这些数据包时如果读到了数据包A的一部分就是半包。如果是读到了数据包A和数据包B&#xf…

从网易云音乐宕机事件看软件危机管理-如何保持服务稳定性

引言:当音乐突然停止 想象一下,你正沉浸在网易云音乐精心为你推荐的歌单中,享受着悠闲的周末下午。突然,音乐戛然而止,App反复崩溃,网页上只剩下冰冷的"502 Bad Gateway"。这不是科幻小说的情节,而是2023年8月19日下午真实发生在数百万网易云音乐用户身上的事。 作…

软考-软件设计师(程序设计语言习题)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

「Java 项目详解」API 文档搜索引擎(万字长文)

目录 运行效果 一、项目介绍 一&#xff09;需求介绍 二&#xff09;功能介绍 三&#xff09;实现思路 四&#xff09;项目目标 二、前期准备 一&#xff09;了解正排索引 二&#xff09;了解倒排索引 三&#xff09;获取 Java API 开发文档 四&#xff09;了解分词…

《黑神话:悟空》全球正式上线!美猴王硬核出圈!

8月20日&#xff0c;这一日期注定将被铭记为中国游戏史上的辉煌时刻。国产3A大作《黑神话&#xff1a;悟空》正式上线&#xff0c;以其惊人的市场表现和文化深度引发了全球玩家的狂热追捧。这款游戏不仅在国内市场引起了强烈反响&#xff0c;更是在全球范围内掀起了一股不可阻挡…

日元回升,澳元强势,市场静待央行指引

一、美元疲软&#xff0c;日元强势反弹 近期&#xff0c;美国就业数据的大幅下修为市场带来经济衰退隐忧&#xff0c;同时增强了美联储9月降息的预期。在此背景下&#xff0c;美元走势疲软&#xff0c;而日元则借机延续回升势头。周三&#xff0c;美元兑日元一度跌至144.44&a…

Apache SeaTunnel数据处理引擎适配的演进和规划

作者 | Chao Tian (tyrantlucifer)&#xff0c;Apache SeaTunnel PMC Member 摘要 Apache SeaTunnel作为一个高性能数据同步工具&#xff0c;以其高效的数据处理能力&#xff0c;为数据集成领域带来了创新。在引擎上&#xff0c;Apache SeaTunnel除了支持自身的Zeta引擎外&am…

UVa1668/LA6039 Let’s Go Green

UVa1668/LA6039 Let’s Go Green 题目链接题意分析AC 代码 题目链接 本题是2012年icpc亚洲区域赛雅加达(Jakarta)赛区的题目 题意 输入一棵n&#xff08;2≤n≤100000&#xff09;个结点的树&#xff0c;每条边上都有一个权值。要求用最少的路径覆盖这些边&#xff0c;使得每条…

JAVA家政服务独立多端平台服务系统小程序源码

解锁现代生活新方式✨ —— "家政服务独立用户多端平台系统"全攻略&#x1f3e0; &#x1f680;【开篇&#xff1a;告别繁琐&#xff0c;拥抱智能家政新时代】 在这个快节奏的时代&#xff0c;谁不想回家就能享受一份宁静与舒适呢&#xff1f;但忙碌的工作、琐碎的…

算法日记day 45(单调栈之每日温度|接雨水)

一、每日温度 题目&#xff1a; 给定一个整数数组 temperatures &#xff0c;表示每天的温度&#xff0c;返回一个数组 answer &#xff0c;其中 answer[i] 是指对于第 i 天&#xff0c;下一个更高温度出现在几天后。如果气温在这之后都不会升高&#xff0c;请在该位置用 0 来…

基本数据类型 --- 浮点型

float的机器码表示&#xff1a; 一个float数据 (pow(-1, sign) fraction) * pow(2, exponent - 127) 由上图&#xff0c;可得&#xff1a; (pow(-1, sign) fraction) * pow(2, exponent - 127) ( 1 2^(-2) ) * pow(2, 124-127) 0.15625 其他文章&#xff1a; https://b…

Go第一个程序

package mainimport "fmt"func main() {str : "hello go"fmt.Println(str) }上述很简单&#xff0c;如何使用os包获取命令行参数呢&#xff1f; package mainimport ("fmt""os" )func main() {fmt.Println(os.Args)str : "hello…

typora激活流程

1.安装typora Typora中文官网&#xff1a;Typora 官方中文站 Typora官网&#xff1a;https://typora.io/releases/all 2.打开软件安装位置 找到路径Typora\resources\page-dist\static\js的js文件&#xff0c;使用记事本编辑打开 替换 CtrlF查找 e.hasActivated"true&…