【并发知识点】AQS的实现原理及应用

news/2024/11/30 15:27:09/

系列文章目录

AQS的实现原理及应用
CAS的实现原理及应用

在这里插入图片描述


文章目录

  • 系列文章目录
  • 前言
  • 一、AQS是什么?
    • 1、应用场景
    • 2、优缺点
  • 二、案例应用
    • 1.使用AQS来实现一个简单的互斥锁
    • 2.模拟赛龙舟程序
  • 总结


前言

在Java技术方面,AQS指的是AbstractQueuedSynchronizer(抽象队列同步器)。它是Java并发包中的一个重要组件,可以提供一种基于锁和信号量的同步机制,用于控制多线程之间的访问和共享资源。


一、AQS是什么?

AQS的核心思想是将多线程的进入和退出操作都放入一个FIFO(先进先出)的等待队列中,通过对这个等待队列的管理来控制线程的并发访问和同步。具体来说,AQS通过内部的state状态变量来表示锁或信号量的状态,当state为0时表示没有被占用,当state为1时表示被占用。此外,AQS还提供了一个Condition对象,用于在等待队列中挂起和唤醒线程。

在应用中,开发人员可以通过继承AQS并实现其内部的acquire和release方法来实现自己的同步机制。acquire方法用于获取锁或信号量,当state为0时会将线程加入等待队列中,直到state状态变为1才会获得锁或信号量。release方法则用于释放锁或信号量,并通知等待队列中的线程可以继续执行。

总的来说,AQS是Java并发包中非常重要的一个组件,它为多线程之间的协作提供了一种简单而高效的机制。当然,开发人员需要深入理解AQS的内部实现,才能更好地使用它来实现自己的同步机制。

1、应用场景

AQS的应用场景非常广泛。它可以用于实现各种同步机制,如互斥锁、读写锁、信号量、倒计时器等等。其中最常见的应用就是锁的实现,如ReentrantLock、ReentrantReadWriteLock、StampedLock等。这些锁都是基于AQS实现的,不同的锁通过实现不同的tryAcquire和tryRelease方法来实现不同的同步策略。此外,AQS还可以用于实现自定义同步机制,如实现一个有界队列、一个线程池等等。

2、优缺点

我们来分析一下AQS的优缺点。AQS的主要优点是灵活性、可扩展性和高并发性。它可以非常方便地实现各种同步机制,并且能够自适应地根据不同的应用场景进行优化。但是,AQS的实现比较复杂,需要对锁的实现细节有一定的了解,同时也需要避免出现死锁和饥饿等问题。因此,在使用AQS时需要谨慎操作。

二、案例应用

1.使用AQS来实现一个简单的互斥锁

代码如下(示例):

import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class Mutex {private static class Sync extends AbstractQueuedSynchronizer {// 当state为0时,表示锁没有被占用;当为1时,表示锁已被占用protected boolean isHeldExclusively() {return getState() == 1;}// 尝试获取锁,如果state为0,则获取成功;否则加入等待队列public boolean tryAcquire(int acquires) {if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}// 释放锁protected boolean tryRelease(int releases) {if (getState() == 0) throw new IllegalMonitorStateException();setExclusiveOwnerThread(null);setState(0);return true;}}// 创建一个Sync对象作为锁private final Sync sync = new Sync();// 获取锁public void lock() {sync.acquire(1);}// 释放锁public void unlock() {sync.release(1);}
}

在上面的代码中,我们定义了一个内部类Sync,它继承了AbstractQueuedSynchronizer并重写了其内部的tryAcquire和tryRelease方法。在tryAcquire方法中,我们使用compareAndSetState方法来尝试获取锁,如果state为0,则获取成功,并将当前线程设置为锁拥有者;否则加入等待队列。在tryRelease方法中,我们简单地将state设置为0,并将锁拥有者设置为null。

在Mutex类中,我们将Sync对象作为锁,并实现了lock和unlock方法来获取和释放锁。这样,我们就可以使用Mutex来实现互斥锁的功能了。

2.模拟赛龙舟程序

在这个程序中,我们将使用AQS来实现一个裁判的计时器,模拟一个赛龙舟比赛中多支队伍竞争的场景。每个队伍都会在启动时创建一个独立的线程,并在程序中使用Semaphore来模拟龙舟的运动。同时,程序中还会使用CountDownLatch来控制所有龙舟同时开始比赛,并使用CyclicBarrier来模拟所有队伍完成比赛后的庆祝活动。
代码如下(示例):

import java.util.concurrent.*;public class DragonBoatRace {private static final int TEAM_NUM = 4; // 参赛队伍数private static final int BOAT_NUM = 1; // 龙舟数量private static final Semaphore semaphore = new Semaphore(BOAT_NUM);private static final CountDownLatch startLatch = new CountDownLatch(TEAM_NUM);private static final CyclicBarrier finishBarrier = new CyclicBarrier(TEAM_NUM);public static void main(String[] args) throws InterruptedException {ExecutorService executorService = Executors.newFixedThreadPool(TEAM_NUM);for (int i = 0; i < TEAM_NUM; i++) {executorService.submit(new Team(i + 1));}startLatch.await(); // 等待所有队伍准备就绪System.out.println("比赛开始!");semaphore.acquire(); // 获取龙舟信号量System.out.println("龙舟已经准备好!");Thread.sleep(2000); // 等待2秒,模拟龙舟前进semaphore.release(); // 释放龙舟信号量System.out.println("比赛结束!");finishBarrier.await(); // 等待所有队伍完成比赛System.out.println("所有队伍完成比赛,开始庆祝!");executorService.shutdown(); // 关闭线程池}static class Team implements Runnable {private final int teamId;public Team(int teamId) {this.teamId = teamId;}@Overridepublic void run() {try {Thread.sleep(1000 * teamId); // 模拟队伍准备时间System.out.println("队伍" + teamId + "已准备就绪!");startLatch.countDown(); // 准备就绪,计数器减一semaphore.acquire(); // 获取龙舟信号量System.out.println("队伍" + teamId + "已上船,准备出发!");Thread.sleep(2000); // 等待2秒,模拟龙舟前进System.out.println("队伍" + teamId + "已完成比赛!");} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release(); // 释放龙舟信号量try {finishBarrier.await(); // 等待其他队伍完成比赛System.out.println("队伍" + teamId + "正在庆祝!");} catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}}}}
}

在这个程序中,我们模拟了4个队伍参加赛龙舟比赛。每个队伍都在启动时创建一个独立的线程,并在比赛前等待1~4秒的准备时间。当所有队伍都准备就绪后,裁判发出比赛开始信号,龙舟开始前进。程序中使用Semaphore来控制龙舟数量,每次只有一个队伍可以使用龙舟。当某个队伍完成比赛后,程序会使用CyclicBarrier来等待其他队伍完成比赛,并且进行庆祝活动。

总之,基于AQS的Java多线程程序可以很好地模拟赛龙舟比赛中的多支队伍竞争的场景。通过使用Semaphore、CountDownLatch和CyclicBarrier等多种同步机制,我们可以实现复杂的线程协作和同步操作。


总结

以上就是今天要讲的内容,本文仅仅简单介绍了AQS在Java中的简单应用。

最后祝大家端午快乐,附包粽子图一张
在这里插入图片描述


http://www.ppmy.cn/news/514004.html

相关文章

【二叉树part03】| 104.二叉树的最大深度、559.n叉树的最大深度、111.二叉树的最小深度、222.完全二叉树的节点个数

目录 ✿LeetCode104.二叉树的最大深度❀ ✿LeetCode559.n叉树的最大深度❀ ✿LeetCode111.二叉树的最小深度❀ ✿LeetCode222.完全二叉树的节点个数❀ ✿LeetCode104.二叉树的最大深度❀ 链接&#xff1a;104.二叉树的最大深度 给定一个二叉树&#xff0c;找出其最大深度…

ubuntu安装WPS2019以及解决缺少字体问题

环境&#xff1a;ubuntu22.04.2 LTS 步骤&#xff1a; 1.去官网下载最新的WPS&#xff0c;官网地址如下&#xff1a;WPS Office 2019 for Linux-支持多版本下载_WPS官方网站 2.sudo dpkg -i 安装包.deb 3.安装完成&#xff0c;首次用WPS打开某个文档&#xff0c;会出现如下报…

postgresql 从应用角度看快照snapshot使用,事务隔离控制不再神密

​专栏内容&#xff1a;postgresql内核源码分析 个人主页&#xff1a;我的主页 座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物. 快照使用 快照是事务中使用&#xff0c;配合事务的隔离级别&#xff0c;体现出不同的可见性。…

c语言大作业开题报告,C语言大作业报告.doc

PAGE13 / NUMPAGES21 页 PAGE13 目录 一、 设计题目 二、目标和需求分析 三、开发工具 四、应用平台 五、程序模块 1、游戏盒子 2、2048 3、扫雷 4、贪吃蛇 六、开发日志 七、程序调试及运行 八、程序开发总结 总结&#xff1a;虽然做出来的东西真的没什么技术水平&#xff0c;…

Php:如何在延迟一秒后打印每个结果

我想要类似的东西&#xff1a; for($k0;$k<20;$k){echo $k; }输出&#xff1a; 0 睡眠1秒钟。 1 睡1秒钟。 2 睡1秒钟。 找了半天都是白屏幕一会&#xff0c;最后一下子输出&#xff0c;达不到效果&#xff0c;谁会&#xff0c;期望留言一下&#xff0c; 下面摘抄了…

力扣高频SQL50题(基础版)——第十天

力扣高频SQL50题(基础版)——第十天 1 只出现过一次的最大数字 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出1 1.1.3 示例输入输出2 1.2 示例sql语句 # 查不到时的结果自然就为Null SELECT MAX(t.num) num FROM (SELECT numFROM MyNumbersGROUP By numHAVING count…

大数据Doris(四十七):开启Steam Load记录

文章目录 开启Steam Load记录 一、停止 Doris 集群 二、在 node3-node5 BE 节点上配置 be.conf 三、重新启动 Doris 集群 开启Steam Load记录 后续执行Stream Load 导入任务后&#xff0c;我们会在Doris集群中会查询对应Stream Load任务的情况&#xff0c;默认BE是不记录S…

命令行排序文件夹大小

du -s * | sort -nr | head 选出排在前面的10个&#xff0c; du -s * | sort -nr | tail 选出排在后面的10个。