Java并发编程之AQS

news2025/1/11 18:38:26

目录

  • 一,什么是AQS
  • 二,AQS核心知识
    • 1,核心思想
    • 2,AQS中的共享状态值-state
    • 3, 同步队列为什么称为FIFO
    • 4, Condition队列-单向队列
  • 三,具体实现
    • 1,独占模式下的AQS
    • 2,共享模式下的AQS
    • 3,自定义实现并发同步器

一,什么是AQS

AQS是并发编程中实现同步器的一个框架。架就是说它帮你处理了很大一部分的逻辑,其它功能需要你来扩展。想想你使用Spring框架的场景,Spring帮助开发者实现IOC容器的bean依赖管理,标签解析等,我们只需要对bean进行配置即可,其他不用管。
AQS基于一个FIFO双向队列实现,被设计给那些依赖一个代表状态的原子int值的同步器使用。我们都知道,既然叫同步器,那个肯定有个代表同步状态(临界资源)的东西,在AQS中即为一个叫state的int值,该值通过CAS进行原子修改。

二,AQS核心知识

1,核心思想

如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。如图所示:
在这里插入图片描述
Sync queue: 同步队列,是一个双向列表。包括head节点和tail节点。head节点主要用作后续的调度。
Condition queue: 非必须,单向列表。当程序中存在cindition的时候才会存在此列表。

2,AQS中的共享状态值-state

之前提到,AQS是基于一个共享的int类型的state值来实现同步器同步的,其声明如下:

 /**
 * 同步状态值
 */
private volatile int state;
/**
 * 获取同步状态值
 */
protected final int getState() {
    return state;
}
/**
 * 修改同步状态值
 */
protected final void setState(int newState) {
    state = newState;
}

(1)由源码我们可以看出,AQS声明了一个int类型的state值,为了达到多线程同步的功能,必然对该值的修改必须多线程可见,因此,state采用volatile修饰,**而且getState()和setState()方法采用final进行修饰,**目的是限制AQS的子类只能调用这两个方法对state的值进行设置和获取,而不能对其进行重写自定义设置/获取逻辑。
(2)AQS中提供对state值修改的方法不仅仅只有setState()和getState(),还有诸如采用CAS机制进行设置的compareAndSetState()方法,同样,该方法也是采用final修饰的,不允许子类重写,只能调用

3, 同步队列为什么称为FIFO

因为只有前驱节点是head节点的节点才能被首先唤醒去进行同步状态的获取。当该节点获取到同步状态时,它会清除自己的值,将自己作为head节点,以便唤醒下一个节点。

4, Condition队列-单向队列

除了同步队列之外,AQS中还存在Condition队列,这是一个单向队列。调用ConditionObject.await()方法,能够将当前线程封装成Node加入到Condition队列的末尾,然后将获取的同步状态释放(即修改同步状态的值,唤醒在同步队列中的线程)。
Condition队列也是FIFO。调用ConditionObject.signal()方法,能够唤醒firstWaiter节点,将其添加到同步队列末尾。

三,具体实现

1,独占模式下的AQS

所谓独占模式,即只允许一个线程获取同步状态,当这个线程还没有释放同步状态时,其他线程是获取不了的,只能加入到同步队列,进行等待。

很明显,我们可以将state的初始值设为0,表示空闲。当一个线程获取到同步状态时,利用CAS操作让state加1,表示非空闲,那么其他线程就只能等待了。释放同步状态时,不需要CAS操作,因为独占模式下只有一个线程能获取到同步状态。ReentrantLock、CyclicBarrier正是基于此设计的。
在这里插入图片描述

独占模式下的AQS是不响应中断的,指的是加入到同步队列中的线程,如果因为中断而被唤醒的话,不会立即返回,并且抛出InterruptedException。而是再次去判断其前驱节点是否为head节点,决定是否争抢同步状态。如果其前驱节点不是head节点或者争抢同步状态失败,那么再次挂起。

2,共享模式下的AQS

共享模式,当然是允许多个线程同时获取到同步状态,共享模式下的AQS也是不响应中断的.
  • 很明显,我们可以将state的初始值设为N(N > 0),表示空闲。每当一个线程获取到同步状态时,就利用CAS操作让state减1,直到减到0表示非空闲,其他线程就只能加入到同步队列,进行等待。释放同步状态时,需要CAS操作,因为共享模式下,有多个线程能获取到同步状态。CountDownLatch、Semaphore正是基于此设计的。

例如,CountDownLatch,任务分为N个子线程去执行,同步状态state也初始化为N(注意N要与线程个数一致):

在这里插入图片描述

3,自定义实现并发同步器

在构建自定义同步器时,只需要依赖AQS底层再实现共享资源state的获取与释放操作即可。自定义同步器实现时主要实现以下几种方法:

  • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
  • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
  • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
  • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
  • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。
class Mutex implements Lock, java.io.Serializable {
    // 自定义同步器
    private static class Sync extends AbstractQueuedSynchronizer {
        // 判断是否锁定状态
        protected boolean isHeldExclusively() {
            return getState() == 1;
        }
        // 尝试获取资源,立即返回。成功则返回true,否则false。
        public boolean tryAcquire(int acquires) {
            assert acquires == 1; // 这里限定只能为1个量
            if (compareAndSetState(0, 1)) {//state为0才设置为1,不可重入!
                setExclusiveOwnerThread(Thread.currentThread());//设置为当前线程独占资源
                return true;
            }
            return false;
        }
        // 尝试释放资源,立即返回。成功则为true,否则false。
        protected boolean tryRelease(int releases) {
            assert releases == 1; // 限定为1个量
            if (getState() == 0)//既然来释放,那肯定就是已占有状态了。只是为了保险,多层判断!
                throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);//释放资源,放弃占有状态
            return true;
        }
    }
    // 真正同步类的实现都依赖继承于AQS的自定义同步器!
    private final Sync sync = new Sync();
    //lock<-->acquire。两者语义一样:获取资源,即便等待,直到成功才返回。
    public void lock() {
        sync.acquire(1);
    }
    //tryLock<-->tryAcquire。两者语义一样:尝试获取资源,要求立即返回。成功则为true,失败则为false。
    public boolean tryLock() {
        return sync.tryAcquire(1);
    }
    //unlock<-->release。两者语文一样:释放资源。
    public void unlock() {
        sync.release(1);
    }
    //锁是否占有状态
    public boolean isLocked() {
        return sync.isHeldExclusively();
    }
}

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

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

相关文章

RdViewer远控隐蔽利用及钓鱼攻击

本文转载于&#xff1a; https://mp.weixin.qq.com/s?__bizMzg4NzkwMDA5NQ&mid2247484000&idx1&sn56b24135aa0aa77a690ff29566341c4e&chksmcf8210b0f8f599a6eaa7743bc65ad4e79400839c40289a8f5407e9732e22a4ae693c0701d1b0&mpshare1&scene23&srci…

未来城市的无限可能

生命体&#xff1a;Mix ta没有棱角但又泾渭分明 冰冷而又生机勃勃 最近受《环球》杂志记者邀请&#xff0c;对未来城市展开了若干讨论&#xff0c;分享给大家&#xff1a; 《环球》杂志 未来城市是什么样子的&#xff1f;请用几个关键词或几句话描述。 我理想中的未来城市应该具…

蒙特卡洛方法的基本介绍和简单应用(求圆周率和定积分)

目录 一、什么是蒙特卡洛方法 二、蒙特卡洛方法的基本思想 三、用蒙特卡洛方法求圆周率 π 四、用蒙特卡洛方法求定积分 你听说过 "蒙特卡洛法" 吗&#xff1f;哦&#xff0c;那是一种计算不规则图形面积的计算机程序算法&#xff0c;具体做法是在软件中用大量的…

【银河麒麟V10】【服务器】麒麟容器常见问题

一、麒麟容器镜像下载链接 &#xff08;1&#xff09;kylin-V10-SP1-0711-x86_64 docker镜像下载链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/10WyBYRoOykqgnagjnoxdXw?pwdt4h5 提取码&#xff1a;t4h5 二、容器基础使用 #导入镜像 docker load < k…

6.100ASK_V853-PRO开发板支持MIPI摄像头

0.前言 ​ 100ASK_V853-PRO开发板支持4LINE的MIPI摄像头和2LINE的MIPI摄像头&#xff0c;使用百问网提供的Tina SDK包生成的镜像&#xff0c;系统已经配置好了&#xff0c;可以直接使用。本章介绍如何去适配一个MIPI摄像头&#xff0c;本文所用的2LINE的MIPI摄像头&#xff0c…

什么是应用交付网络(ADN)

从CDN到ADN CDN&#xff08;内容分发网络&#xff09;在90年代末受到麻省理工学院的启发并完成发明&#xff0c;00年代初成立第一家成功的CDN商业企业Akamai。CDN的目标是相对于最终用户在空间上分配服务&#xff0c;以提供高可用性和高性能。随着互联网的发展&#xff0c;CDN…

建筑行业搭建BI数据可视化平台,已成为大势所趋

建筑行业的项目管理是一个系统而复杂的过程&#xff0c;其重点主要是寻求造价、质量、工期等几个方面的平衡点&#xff0c;并且对项目的整个过程必须要有一个清晰和直观的了解。 因此可以通过BI数据可视化分析将各个节点的系统数据、业务数据完整的呈现&#xff0c;将各管理层…

数字孪生园区可视化大屏系统-广州华锐互动

工业园区是现代工业化生产的重要组成部分&#xff0c;也是推动经济发展和提升城市形象的重要载体。而数字孪生园区可视化大屏系统作为一种新兴的技术应用&#xff0c;可以为工业园区的建设、管理和发展带来很大的价值。 首先&#xff0c;数字孪生园区可视化大屏系统可以帮助工业…

[C++]普通二叉搜索树实现

目录 1 二叉搜索树的基本概念 2 二叉搜索树的构建 2.1 二叉搜索树的结点 2.2 搜索树类的结构 3 成员函数 3.1 插入 3.2 查找 3.3 删除&#xff08;重点&#xff09; 3.4 默认成员函数的辅助函数 4 普通的二叉搜索树的效率 1 二叉搜索树的基本概念 二叉搜索树又称二叉…

Java框架学习05(Spring事务详解)

1、什么是事务&#xff1f; 事务是逻辑上的一组操作&#xff0c;要么都执行&#xff0c;要么都不执行。 我们系统的每个业务方法可能包括了多个原子性的数据库操作&#xff0c;比如下面的 savePerson() 方法中就有两个原子性的数据库操作。这些原子性的数据库操作是有依赖的&…

相亲交友app开发上线运营的整个流程是什么

一、相亲交友app开发基本流程 1、需求分析&#xff1a;需求分析是相亲交友app源码开发的第一步&#xff0c;也是最重要的一步。在需求分析阶段&#xff0c;可以了解客户对于系统的需求&#xff0c;确定系统功能实现的大致方向和功能。 2、系统架构&#xff1a;系统架构阶段就是…

这里有一份教你每天用领英获取20个询盘的免费课程,手慢无

于2023年3月22日&#xff0c;我们圆满完成了深圳宝安的外贸分享交流会&#xff0c;时隔两个月即将迎来我们的广州场。 在上次深圳会议&#xff0c;有幸邀请到江西省跨境电商协会会长莅临 给大家分享了&#xff1a; 如何帮助传统制造业从“0”开始做外贸、如何借助平台为企业…

浅谈霍尔电流传感器在电池柜监测中的应用

安科瑞 耿敏花 摘要&#xff1a;本文分析了霍尔电流传感器的工作原理&#xff0c;浅谈其在电池柜监测中的应用。 关键词&#xff1a;霍尔电流传感器 工作原理 充放电电流 电池柜 引言 大多数的工厂里&#xff0c;使用到的电池柜&#xff0c;它是将许多的新组装的电池一起…

不合格机器人工程讲师为何不分享成功的案例

不合格机器人工程讲师如何坦然面对失败 除了失败&#xff0c;更多的失败&#xff0c;也并非一无所获。 博客分享过&#xff0c;但是关注度&#xff08;浏览量&#xff09;不高&#xff0c;大部分成功案例都是学生/毕业生自身努力的结果&#xff0c;教育引导的作用小于他们自身…

中国品牌日:海尔智家向世界展示“中国”

品牌&#xff0c;在任何时候都是一个厚重的话题。什么是品牌&#xff1f;被咬掉一口的苹果、圆润张扬的对号、还有那个大大的黄色M&#xff0c;在诞生之初也不过是个商标。只是后来&#xff0c;它们跟智能手机、体育和快餐划上了等号&#xff0c;讲出了故事、收获了口碑&#x…

Android Framework——Binder 监控方案

作者&#xff1a;低性能JsonCodec 在 Android 应用开发中&#xff0c;Binder 可以说是使用最为普遍的 IPC 机制了。我们考虑监控 Binder 这一 IPC 机制&#xff0c;一般是出于以下两个目的&#xff1a; 卡顿优化&#xff1a;IPC 流程完整链路较长&#xff0c;且依赖于其他进程…

操作系统基础知识介绍之内存层次结构(一)

传统上&#xff0c;内存层次结构的设计者专注于优化平均内存访问时间&#xff0c;这由缓存访问时间、未命中率和未命中惩罚决定。 然而&#xff0c;最近&#xff0c;功率已成为主要考虑因素。 在高端微处理器中&#xff0c;可能有 60 MiB 或更多的片上高速缓存&#xff0c;并且…

并查集-- 一种路径压缩实现

并查集用于计算图连通分量。 比如回答这样的问题&#xff1a; 社交媒体中&#xff0c;用户A和用户B是否属于同一个圈子里的&#xff1f;一个城市到另一个城市是否是可达的&#xff1f; 并查集适用于并不需要计算出图上具体的路径&#xff0c;只需要计算是否连通。 public i…

JavaScript 链表

&#xff08;成功的唯一秘诀——坚持最终一分钟。——柏拉图&#xff09; 链表 众所周知&#xff0c;数组的查询比链表快&#xff0c;但插入比链表慢。 这是因为链表是一种动态的数据结构&#xff0c;不同于数组的是&#xff0c;链表分配内存空间的灵活性&#xff0c;它不会像…

解决车载U盘:USB设备未连接 问题

U盘是一种常用的便携式存储设备&#xff0c;用于存储和传输数据。在U盘上使用的文件系统类型决定了它可以支持的文件大小、安全性和其他特性。以下是几种常见的U盘文件系统类型&#xff1a; 1. FAT32:这是U盘上最常用的文件系统类型之一。FAT32文件系统支持的最大文件大小为4GB…