深入理解 Java 并发:AbstractQueuedSynchronizer 源码分析

news2025/1/10 10:15:47

序言

在多线程编程中,同步机制是保障线程安全和协调线程之间操作顺序的重要手段。AQS 作为 Java 中同步机制的基础框架,为开发者提供了一个灵活且高效的同步工具。本文将通过对 AQS 源码的分析,解读 AQS 的核心实现原理,并深入探讨其在不同同步组件中的应用。

一、什么是 AQS

workspace (4).jpg

AQS 是 AbstractQueuedSynchronizer 的缩写,是 Java 并发包中提供的一个用于实现各种锁和同步器的基础框架。它提供了一种灵活而强大的机制,可以帮助开发者实现各种自定义的锁和同步组件。

AQS 的设计思想是基于等待队列(Wait Queue)和同步状态(Sync State)。它通过维护一个等待队列来管理等待获取同步资源的线程,并使用一个同步状态来表示当前锁或同步器的状态。AQS 提供了一系列方法来操作同步状态和等待队列,包括获取同步状态、释放同步状态、加入等待队列等操作,以及唤醒等待队列中的线程等操作。

AQS 主要包含两个核心方法:tryAcquire 和 tryRelease。这两个方法由具体的同步器实现,用于控制同步状态的获取和释放。通过这两个方法的协作,AQS 可以实现各种类型的锁和同步器,包括独占锁、共享锁、读写锁、信号量等。

AQS 提供了一种基于条件等待和通知的高级线程同步机制,能够满足各种复杂的并发编程需求。它被广泛应用于 Java 并发编程中的各种锁和同步组件的实现,如 ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore 等。 AQS 提供了一个灵活、高效和可扩展的框架,使得开发者可以轻松地实现自定义的并发控制组件,并充分发挥 Java 并发包提供的丰富功能。

二、AQS 源码结构分析

public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {

    // 构造器
    protected AbstractQueuedSynchronizer() { }

    // Node 内部类
    static final class Node {}

    // 构造链表等待队列的头节点
    private transient volatile Node head;

    // 构造链表等待队列的尾节点
    private transient volatile Node tail;

    // 同步状态
    private volatile int state;

    // tryAcquire 方法
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    // tryRelease 方法
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

    // ConditionObject 内部类
    public class ConditionObject implements Condition, java.io.Serializable {}

    // 省略了其他方法或属性

}

上面是 AbstractQueuedSynchronizer 抽象类的源码,省略了一部分内容,保留了较为核心的内容,我们可以看出其核心结构如下:

  1. 同步状态(state):AQS 内部维护了一个同步状态变量,通常是一个整数,用于表示同步器的状态。这个同步状态是 AQS 控制同步和并发访问的核心。
  2. 等待队列(Wait Queue):AQS 使用等待队列来管理等待获取同步资源的线程。等待队列通常是一个基于 FIFO(先进先出)的数据结构(使用链表实现),确保等待线程的公平竞争和顺序执行。
  3. Node 类:等待队列中的每个节点都是 Node 类的实例,它包含了线程的相关信息以及用于构建队列的链接信息。Node 类中通常包含了指向前驱节点和后继节点的引用,用于构建双向链表。
  4. 核心方法:acquire 和 release:用于获取和释放同步状态的方法,是 AQS 中最核心的方法之一。tryAcquire 和 tryRelease:尝试获取和释放同步状态的方法,是具体同步器需要实现的抽象方法。
  5. 条件对象(ConditionObject):条件对象是 AQS 提供的一种机制,用于管理条件队列。

三、AQS 工作原理

workspace (2).jpg

AQS 的核心思想是基于状态的抽象和等待队列的管理。具体来说,当一个线程尝试获取锁或者同步资源时,如果失败了,它会被包装成一个节点加入到等待队列中,然后自旋等待获取锁。当锁的释放动作发生时,AQS 会按照特定的规则选择一个节点来唤醒,从而实现线程的竞争和同步。

四、AQS 关键方法解析

4.1 acquire() 方法

public final void acquire(int arg) {
if (!tryAcquire(arg) &&
    acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    selfInterrupt();
}

acquire 是 AQS 定义获取锁的方法。该方法看似简单其实做了许多事情。首先,尝试使用 tryAcquire 方法来快速尝试获取同步状态,如果失败则将当前线程加入到等待队列中,并自旋等待直到成功获取同步状态为止。如果线程在等待过程中被中断,则会自我中断。

acquire 方法被 final 关键字修饰了,是不可被重写的。acquire 方法其实只是定义了流程,具体逻辑是下放给 tryAcquire 方法了。

protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}

从上面的源码可以看出 tryAcquire 方法没有具体的实现,需要交由其子类实现。

4.2 release() 方法

public final boolean release(int arg) {
// 尝试释放锁
if (tryRelease(arg)) {
    // 获取当前等待队列的头节点
    Node h = head;
    // 检查头节点是否存在且其等待状态不为 0
    if (h != null && h.waitStatus != 0)
        // 唤醒后继节点
        unparkSuccessor(h);
    // 释放锁成功,返回 true
    return true;
}
// 释放锁失败,返回 false
return false;
}

release 是 AQS 定义释放锁的方法。该方法与获取锁的 acquire 方法类似,只是定义了释放锁的流程,具体的释放逻辑是交由子类去实现 tryRelease 方法。

五、AQS 深入分析

等待队列与部分核心方法上文已经有过解释了。AQS 剩下的核心内容还有:

  1. 同步状态(state)
  2. Node 类
  3. 条件对象(ConditionObject)

image.png

同步状态是一个整数变量,通常被命名为 state。同步状态用于表示当前同步器的状态信息,可以被不同类型的同步器用于不同的目的。具体来说,同步状态可以代表某种资源的数量、某种资源的可用性或者某种锁的状态等。例如,在独占锁(Exclusive Lock)中,同步状态可以表示锁的占用情况;在共享锁(Shared Lock)中,同步状态可以表示锁的共享数量;在 Semaphore(信号量)中,同步状态可以表示可用的许可数量等。

Node 类是 AQS 中的一个内部类,主要用于构建链表形式的等待队列。每个节点都代表一个等待获取同步资源的线程。

在并发编程中,等待唤醒机制是一种重要的线程同步机制,用于实现线程之间的协作和通信。而 AQS 提供了条件队列用于实现等待唤醒机制,条件队列通常与条件对象一起使用,条件对象是 AQS 提供的一种机制,用于管理条件队列

六、AQS 在锁和同步组件中的应用

AQS 只是一个抽象类,我们想要使用它必须要实现相关的逻辑。而 Java 基于 AQS 已经给我们提供了许多的实现:

  1. ReentrantLock(重入锁):ReentrantLock 是 Java 并发包提供的一种独占锁实现,它使用了 AQS 来实现锁的底层逻辑。ReentrantLock 内部持有一个 AQS 实例,通过 AQS 的 acquire 和 release 方法来控制锁的获取和释放,从而实现可重入性和线程安全性。
  2. ReentrantReadWriteLock(重入读写锁):ReentrantReadWriteLock 是 Java 并发包提供的一种读写锁实现,它也使用了 AQS 来实现锁的底层逻辑。ReentrantReadWriteLock 内部维护了两个 AQS 实例,分别用于读锁和写锁的管理。通过 AQS 的 acquire 和 release 方法来控制读锁和写锁的获取和释放,从而实现读写锁的功能。
  3. CountDownLatch(倒计时门栓):CountDownLatch 是 Java 并发包提供的一种同步工具,它使用了 AQS 来实现同步等待的逻辑。CountDownLatch 内部持有一个 AQS 实例,通过 AQS 的 acquire 和 release 方法来实现等待和通知的功能。线程调用 await 方法等待计数器归零,而其他线程调用 countDown 方法来减少计数器的值,当计数器归零时,等待的线程被唤醒继续执行。
  4. Semaphore(信号量):Semaphore 是 Java 并发包提供的一种同步工具,它也使用了 AQS 来实现信号量的逻辑。Semaphore 内部持有一个 AQS 实例,通过 AQS 的 acquire 和 release 方法来控制信号量的获取和释放。线程调用 acquire 方法尝试获取信号量,而其他线程调用 release 方法来释放信号量,从而控制同时访问资源的数量。

推荐阅读

  1. 深入了解 Arthas:Java 应用程序诊断利器
  2. 基于 AI 的数据库助手-Chat2DB
  3. EasyExcel 处理 Excel
  4. 实体映射解决方案-MapStruct
  5. 动态切换数据源的最佳实践

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

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

相关文章

同步互斥问题模型

目录 一. 生产者-消费者问题二. 多生产者-多消费者问题三. 吸烟者问题四. 读者-写者问题五. 哲学家进餐问题 \quad 一. 生产者-消费者问题 \quad 问题分析 \quad 如何实现 \quad \quad V操作不会导致进程阻塞,因此两个v操作顺序可以交换。 \quad 二. 多生产者-多消费…

前端 -- 基础 表单标签 - 表单元素 < Select > < textarea >

在表单域中可以定义各种表单元素,这些表单元素就是允许用户在表单中输入或者选择的内容控件。 在之前,我们就讲过 表单元素 分了 三类来讲 : 1 input 输入表单元素 2 select 下拉表单元素 3 textarea 文本域元素 input 表单元素 我们…

AGI要闻:斯坦福李飞飞首次创业,瞄准“空间智能”;OpenAI下周发布搜索产品挑战谷歌;新的开源 AI 眼镜来了|钛媒体AGI | 最新快讯

多方消息证实,OpenAI将会在北京时间5月10日(周五)凌晨2点公布搜索引擎新产品消息。 斯坦福大学首位红杉讲席教授 李飞飞 通用人工智能(AGI)领域又公布了一系列重磅消息。 5月4日凌晨,据路透社&#xff0c…

宝塔怎么配置nginx

宝塔怎么配置nginx 1.找到nginx配置位置 2.修改nginx.conf文件 3.重启nginx

在视频中使用时间卷积和半监督训练进行三维人体姿态估计

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 摘要Abstract文献阅读:在视频中使用时间卷积和半监督训练进行三维人体姿态估计1、文献摘要2、提出方法2.1、时间扩张卷积模型2.2、半监督方法2.3、与传统…

Mac OS系统如何更新

用了好几年的Mac Book安装软件经常提示需要更高的系统版本,因此要升级系统版本,但是开始在系统设置里面找了一下没有找到升级的按钮,找了资料后才知道如何升级。有以下两种入口 一、App Store搜索MacOs,在出现的搜索结果中选择下载…

力扣例题(接雨水)

链接: . - 力扣(LeetCode) 题目描述: 思路: 判断一块地方是否可以接到雨水,只需要判断他是否有左右边界使他可以接到水 左右边界分别为此处左侧的最高点和右侧的最高点 同时此处可接雨水的高度为左右两…

离散数学之命题逻辑思维导图+大纲笔记(预习、期末复习,考研,)

大纲笔记: 命题逻辑的基本概念 命题与联结词 命题 命题是推理的基本单位 真命题,假命题 特征 陈述句 唯一的真值 是非真即假的陈述句 非命题 疑问句 祈使句 可真可假 悖论 模糊性 三个基本概念 复合命题 真值取决于原子命题的值和逻辑联结词 原子命题 逻…

微图乐 多种装B截图一键制作工具(仅供娱乐交流)

软件介绍 采用exe进程交互通信。全新UI界面,让界面更加清爽简约。支持zfb、VX、TX、Yin行、Dai款、游戏等图片生成,一键超清原图复制到剪辑板,分享给好友。适用于提高商家信誉度,产品销售额度。装逼娱乐,用微图乐。图…

14_Scala面向对象编程_属性

属性 1.类中属性声明 // 1.给Scala声明属性;var name :String "zhangsan"val age :Int 302.系统默认赋值 scala由于初始化变量必须赋值,为了解决此问题可以采用下划线赋值,表示系统默认赋值 , –但是此方法局限于变量&…

Vue ui 创建vue项目,详细使用攻略。

1.安装及启动 1.1 Vue ui 使用前提是全局安装vue.js 命令如下 npm install vue -g 1.2 安装过Vue.js 之后 随便在自己系统的一个地方打开命令面板 1.3 使用命令启动vue ui面板创建项目 vue ui 如图运行后显示这种就是启动成功,成功之后会弹出页面或者直接访问你的…

【业务场景】京东实际场景,频繁GC引起的CPU飙高问题的解决

目录 1.业务介绍 2.判断任务类型 3.CPU飙高的原因 1.业务介绍 本文的业务场景是京东零售线公开的一篇文章,文章内容详细介绍了京东零售线如何将广告相关的定时任务从半小时优化到秒级的,原文链接: 半小时到秒级,京东零售定时…

Pandas标签库

目录 1.创建对象 1.一维对象 1.字典创建法 2.数组创建法 2.二维对象 1.字典创建法 2.数组创建法 2.对象的索引 1.一维对象的索引 1.查询 2.切片 2.二维对象的索引 1.访问 2.修改 3.对象的变形 1.对象的转置 2.上下翻转和左右翻转 3.对象的重塑 4.一维对象的合…

使用Jellyfin创建媒体库

目录 前言 1.介绍Jellyfin 2.安装 3.设置时注意点 4.效果 5.内存占用 前言 分为客户端和服务器端,这里讲的是服务器端的安装 1.介绍Jellyfin Jellyfin 是一个免费开源的媒体服务器软件,它允许您管理和播放您的媒体文件。这些媒体文件可以包括电…

Vue3 + Vite + TypeScript + Element-Plus创建管理系统项目

官方文档 Vue3官网 Vite官方中文文档 创建项目 使用npm命令创建项目: npm create vitelatest输入项目名称: ? Project name:项目名选择vue: ? Select a framework: - Use arrow-keys. Return to submit.Vanilla > VueReactPrea…

AST原理(反混淆)

一、AST原理 jscode var a "\u0068\u0065\u006c\u006c\u006f\u002c\u0041\u0053\u0054";在上述代码中,a 是一个变量,它被赋值为一个由 Unicode 转义序列组成的字符串。Unicode 转义序列在 JavaScript 中以 \u 开头,后跟四个十六进…

【Linux】 OpenSSH_7.4p1 升级到 OpenSSH_9.6p1(亲测无问题,建议收藏)

👨‍🎓博主简介 🏅CSDN博客专家   🏅云计算领域优质创作者   🏅华为云开发者社区专家博主   🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入&#xff01…

【linuxC语言】vfork、wait与waitpid函数

文章目录 前言一、函数使用1.1 vfork1.2 wait1.3 waitpid 二、示例代码总结 前言 在Linux系统编程中,vfork()、wait() 和 waitpid() 函数是处理进程管理和控制流的重要工具。这些函数允许我们创建新进程、等待子进程结束并获取其退出状态,从而实现进程间…

【CAP探索者指南】掌握分布式世界的三角平衡术,一致性、可用性、分区容错性大揭秘!

关注微信公众号 “程序员小胖” 每日技术干货,第一时间送达! 引言 在现代的微服务架构中,系统被拆分成了许多小型服务,每个服务可能有自己的数据库。这种架构带来了灵活性和可扩展性,但也引入了新的挑战,…

高质量数据至关重要:phi-1.5论文笔记

导语 phi-系列模型是微软研究团队推出的轻量级人工智能模型,旨在实现“小而精”的目标,能够实现在低功耗设备上例如智能手机和平板电脑上部署运行。截止目前,已经发布到了phi-3模型,本系列博客将沿着最初的phi-1到phi-1.5&#x…