并发 - AQS 与 Volatile

news/2026/1/21 16:12:25/文章来源:https://www.cnblogs.com/liushoushou/p/19512378

知识点 5.5:并发编程基石 —— AQS 与 Volatile

在深入了解各种锁和同步工具之前,必须先理解 JUC 框架的两个核心基石:volatile 关键字和 AQS 框架。


1. volatile 关键字:并发编程的“信号旗”

volatile 是一个 Java 变量修饰符,它非常轻量,但提供了两个至关重要的保证。

1.1 保证可见性 (Visibility)

  • 问题所在: 在多核 CPU 架构下,每个 CPU 都有自己的高速缓存(Cache)。当一个线程(运行在 CPU-1 上)修改了一个共享变量的值,它可能只是更新了自己的缓存,而没有立刻写回主内存。此时,另一个线程(运行在 CPU-2 上)读取这个变量时,可能会从自己的缓存里读到一个过时的、旧的值,导致数据不一致。

  • volatile 的作用: 当一个变量被声明为 volatile 后,JVM 会保证:

    1. 写操作:对这个变量的修改会立即被刷新到主内存中。
    2. 读操作:每次读取这个变量,都会强制从主内存中重新加载,而不是使用线程本地的缓存。
  • 生活比喻: volatile 变量就像一个公共的、唯一的电子公告牌。当一个线程更新了公告牌上的内容,其他所有线程都能立刻看到最新的内容,而不是看自己手里抄录的、可能已经过时的小纸条。

1.2 禁止指令重排序 (Instruction Reordering)

  • 问题所在: 为了提高性能,编译器和 CPU 可能会在不影响单线程最终结果的前提下,打乱代码的执行顺序。但在多线程环境下,这种重排序可能会导致意想不到的错误(例如著名的“双重检查锁定”单例模式失效问题)。

  • volatile 的作用: volatile 关键字会作为一个“内存屏障”(Memory Barrier)。当程序执行到 volatile 变量的读或写操作时,它前面的所有操作都必须已经完成,且结果对后续操作可见;它后面的所有操作也必须在它之后才能开始。这有效地阻止了指令重排序跨越这个屏障。

1.3 volatile 不能保证原子性

这是 volatile 最重要的一个局限性,也是面试高频陷阱。

  • 原子性: 指一个操作是不可分割、不可中断的。要么完全执行成功,要么完全不执行。

  • count++ 的问题: 这个操作看似一步,实际包含三步:“1. 读取 count 的值;2. 将值加 1;3. 将新值写回 count”。volatile 无法保证这三步作为一个整体的原子性。

演示代码:VolatileNoAtomicDemo.java

以下代码清晰地演示了 volatile 无法保证复合操作(如 ++)的原子性。期望结果是 100,000,但实际运行结果通常会小于此值,证明有自增操作丢失。


package com.study.concurrency;import java.util.concurrent.atomic.AtomicInteger; // 引入 AtomicInteger/*** 演示 volatile 不保证原子性的经典例子:多线程计数器* 运行结果:最终 count < 期望结果,证明 volatile 无法保证“++”操作的原子性*/
public class VolatileNoAtomicDemo {// 原始问题:使用 volatile 修饰共享变量,但 ++ 操作非原子private static volatile int count = 0; // 此处用 volatile 无法解决原子性问题// 用于解决原子性问题的方案一:使用 AtomicInteger// private static AtomicInteger atomicCount = new AtomicInteger(0);// 用于解决原子性问题的方案二:使用 synchronized 块// private static final Object lock = new Object();private static final int THREADS = 10;private static final int INCREMENTS_PER_THREAD = 10_000;public static void main(String[] args) throws InterruptedException {Thread[] threads = new Thread[THREADS];for (int i = 0; i < THREADS; i++) {threads[i] = new Thread(() -> {for (int j = 0; j < INCREMENTS_PER_THREAD; j++) {// 原始问题代码:count++; // 非原子操作:读-改-写三步// 方案一修正:使用 AtomicInteger// atomicCount.incrementAndGet(); // 原子操作// 方案二修正:使用 synchronized// synchronized (lock) { // 或者 synchronized (VolatileNoAtomicDemo.class)//     count++;// }}});threads[i].start();}for (Thread t : threads) {t.join();}System.out.println("期望结果 = " + (THREADS * INCREMENTS_PER_THREAD));// System.out.println("实际结果 (volatile) = " + count); // 如果使用 volatile int countSystem.out.println("实际结果 = " + count); // 或者使用 atomicCount.get()}
}

问题分析:为什么 volatile 会失效?

  • volatile 只能保证可见性有序性,即保证读取到 count 的值是最新的,并且阻止了指令重排序。

  • 但是,count++ 并非一个单一的原子操作。当多个线程同时进行“读取-修改-写入”这三个步骤时,即使它们都读取到了最新的值,也可能在修改和写入的间隙被其他线程抢占 CPU,导致更新丢失。例如:

    1. 线程A 读取 count (值为 100)。
    2. 线程B 读取 count (此时 count 仍是 100,因为线程A还没写回)。
    3. 线程A 将 count 计算为 101,并写入主内存。
    4. 线程B 将 count 计算为 101,并写入主内存。
    • 结果:count 最终是 101,而不是预期的 102。一个自增操作丢失。

如何修正这个问题来保证线程安全?
要保证 count++ 这样的复合操作的原子性,必须使用更强大的同步机制:

  1. 使用 synchronized 关键字

    • count++ 操作放入 synchronized 代码块中。synchronized 保证了同一时间只有一个线程可以进入该代码块,从而使整个“读-改-写”序列变为原子操作。
    // 示例:
    // synchronized (VolatileNoAtomicDemo.class) { // 或 synchronized (任意共享对象)
    //     count++;
    // }
    
    • synchronized 能够保证原子性、可见性、有序性。但它是一个重量级锁,在高并发竞争时性能开销较大。
  2. 使用 java.util.concurrent.atomic 包下的原子类 (推荐)

    • 对于简单的原子操作(如整数自增、布尔值设置等),Java 提供了 AtomicInteger, AtomicLong, AtomicBoolean 等原子类。
    • 这些类内部通过CAS (Compare-And-Swap) 等无锁机制来保证操作的原子性,性能通常比 synchronized 更好。
    // 示例:
    // private static AtomicInteger count = new AtomicInteger(0);
    // ...
    // count.incrementAndGet(); // 原子性地执行 count++
    
    • 结论volatile 适用于一写多读的场景,或者当变量的更新不依赖于其当前值时。对于 count++ 这样的复合操作,必须使用 synchronizedAtomicInteger 这样的原子类来保证线程安全。

2. AQS (AbstractQueuedSynchronizer):JUC 的龙骨

AQS 是 JUC 中绝大部分锁和同步工具的核心实现框架。它是一个抽象类,本身不是一个锁,而是用来构建锁和同步器的基础骨架。

2.1 AQS 的核心设计思想

AQS的核心思想是一种模板方法模式的应用。它提供了一个同步状态管理state)和线程排队(FIFO队列)的基础框架。开发者只需实现对state的获取和释放逻辑,而线程的排队、阻塞、唤醒等复杂操作都由AQS框架本身来完成。

2.2 两大核心数据结构

  1. volatile int state: 一个用 volatile 修饰的整型变量,用于表示同步状态

    • state 的含义由具体实现类来定义。例如,在ReentrantLock中,它表示锁的重入次数;在Semaphore中,它表示剩余的许可数量。
    • AQS通过getState(), setState(), compareAndSetState()这三个方法来原子地操作这个状态。
  2. 一个 FIFO 的线程等待队列 (CLH 队列):

    • 这是一个双向链表,用于存放所有等待获取同步状态的线程。
    • 当一个线程获取锁失败后,它就会被封装成一个Node节点,加入到这个队列的尾部并被挂起,等待被唤醒。

2.3 底层实现:线程如何排队与唤醒?

这是AQS的精髓所在,也是面试深度考察点。

  1. 线程安全入队

    • 当一个新线程获取锁失败需要入队时,它会被封装成一个Node节点。
    • AQS通过自旋(for循环)+ CAS(Compare-And-Swap)这种无锁的方式,来原子性地将新节点添加到等待队列的队尾。这避免了在入队这个高并发操作上加锁,提高了效率。
  2. 线程挂起(阻塞)

    • 线程成功入队后,AQS会调用 LockSupport.park(this) 方法将当前线程挂起。
    • LockSupport是JUC包中的一个工具类,它提供的park()/unpark()相比于Object.wait()/notify(),优点在于无需获取对象的监视器锁,并且可以unparkpark而不会丢失信号。
  3. 线程唤醒

    • 当持有锁的线程调用release()方法释放同步状态时,它会通过LockSupport.unpark()方法唤醒等待队列头部的下一个节点所对应的线程
    • 被唤醒的线程会再次尝试获取同步状态(state)。

2.4 独占模式 vs. 共享模式

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

  • 独占模式 (Exclusive Mode):资源在同一时刻只能被一个线程持有。

    • 例子ReentrantLock
  • 共享模式 (Shared Mode):资源在同一时刻可以被多个线程持有。

    • 例子Semaphore(信号量)、CountDownLatchReadWriteLock中的读锁。

2.5 需要重写的核心方法 (模板方法)

开发者在实现自定义同步器时,需要根据选择的模式重写以下核心方法:

  • 独占模式

    • tryAcquire(int arg): 尝试以独占方式获取资源。
    • tryRelease(int arg): 尝试以独占方式释放资源。
    • isHeldExclusively(): 判断当前线程是否是独占持有资源。
  • 共享模式

    • tryAcquireShared(int arg): 尝试以共享方式获取资源。
    • tryReleaseShared(int arg): 尝试以共享方式释放资源。

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

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

相关文章

上海AI公司推荐哪家好?权威推荐六家头部AI企业!

在人工智能逐渐从“技术突破期”迈向“产业兑现期”的背景下,市场对AI公司的评价标准正在发生根本性变化。单一算法能力、模型参数规模,已不足以支撑长期竞争力,系统能力、工程化水平与行业适配深度正成为核心判断依…

【Dify高级用法】:Iteration节点处理数组数据的3种高阶模式

第一章&#xff1a;Iteration节点的核心机制与数据流原理 基本概念与运行模型 Iteration节点是工作流系统中用于实现循环处理的关键组件&#xff0c;其核心功能是在满足特定条件时重复执行一组操作。该节点通过接收输入数据流&#xff0c;并在每次迭代中处理一个元素&#xff…

你还在用API模拟?MCP协议让AI直接操作文件系统(性能提升300%的秘密)

第一章&#xff1a;MCP协议与AI Agent协同操作文件系统的新范式 在分布式智能系统快速演进的背景下&#xff0c;MCP&#xff08;Multi-agent Coordination Protocol&#xff09;协议为AI Agent之间高效、安全地协同操作文件系统提供了全新的通信架构。该协议通过定义标准化的消…

MCP协议权限配置全解析:确保AI Agent安全访问本地文件的7个关键步骤

第一章&#xff1a;MCP协议与AI Agent文件操作概述 在现代分布式系统中&#xff0c;MCP&#xff08;Machine Communication Protocol&#xff09;协议作为一种高效、轻量级的通信规范&#xff0c;广泛应用于AI Agent之间的数据交换与协同任务处理。该协议定义了消息格式、传输机…

Z-Image-Turbo成本控制:短时任务GPU按需启动实战指南

Z-Image-Turbo成本控制&#xff1a;短时任务GPU按需启动实战指南 1. 引言&#xff1a;为什么需要为Z-Image-Turbo做成本优化&#xff1f; 你是不是也有这样的困扰&#xff1a;想用AI生成几张图&#xff0c;结果发现GPU服务器一开就是一天&#xff0c;哪怕只用了十分钟&#x…

详细介绍:解锁Python的强大能力:深入理解描述符

详细介绍:解锁Python的强大能力:深入理解描述符pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas",…

物联网墨水屏电子价签可以支持NFC刷新吗?

随着通信技术的持续演进&#xff0c;Wi-Fi、蓝牙、NFC乃至新兴的星闪等技术不断推动物联网设备的连接方式多样化&#xff0c;也为各类智能硬件提供了灵活更新与管理的可能。在零售、仓储、办公等场景中广泛应用的墨水屏电子价签&#xff0c;其信息刷新方式自然成为用户关注的焦…

2026年复合管激光堆焊厂家排名,广东好用的厂家推荐

本榜单依托全维度市场调研与真实行业口碑,深度筛选出五家防腐防磨堆焊领域标杆企业,为工业客户选型提供客观依据,助力精准匹配适配的服务伙伴。 TOP1 推荐:广东博盈特焊技术股份有限公司 推荐指数:★★★★★ | 口…

TurboDiffusion影视应用案例:分镜动态预览系统快速搭建教程

TurboDiffusion影视应用案例&#xff1a;分镜动态预览系统快速搭建教程 1. 引言&#xff1a;为什么影视创作需要TurboDiffusion&#xff1f; 在传统影视制作流程中&#xff0c;分镜预览&#xff08;Animatic&#xff09;是前期策划的关键环节。导演和美术团队通常要花费数小时…

AI推理框架选型指南:SGLang开源优势+GPU适配入门必看

AI推理框架选型指南&#xff1a;SGLang开源优势GPU适配入门必看 在当前大模型快速发展的背景下&#xff0c;如何高效部署和调用LLM&#xff08;大语言模型&#xff09;成为开发者关注的核心问题。传统的推理方式往往面临吞吐低、延迟高、编程复杂等问题&#xff0c;尤其在多轮…

MCP服务器resources动态扩展实践:应对高并发的4步速成方案

第一章&#xff1a;MCP服务器resources动态扩展的核心机制 MCP&#xff08;Microservice Control Plane&#xff09;服务器的 resources 动态扩展机制&#xff0c;是支撑其高可用性与弹性伸缩能力的关键设计。该机制不依赖静态配置或重启生效&#xff0c;而是通过实时感知负载变…

2026朝阳市英语雅思培训辅导机构推荐,2026权威出国雅思课程排行榜

依托英国文化教育协会最新雅思备考数据、朝阳市本地调研,结合朝阳县、建平县、喀喇沁左翼蒙古族自治县考生实战反馈,本次对区域内雅思培训市场开展全面深度测评,旨在破解考生选课难、提分慢、技巧薄弱等核心痛点,为…

低成本部署GPT-OSS-20B?微调显存需求与优化方案

低成本部署GPT-OSS-20B&#xff1f;微调显存需求与优化方案 1. GPT-OSS-20B是什么&#xff0c;为什么值得关注&#xff1f; 你可能已经听说过OpenAI最新开源的GPT-OSS系列模型&#xff0c;而其中的GPT-OSS-20B正成为社区关注的焦点。它不是简单的闭源模型复刻&#xff0c;而是…

2026年权威数据资产变现品牌方案推荐

本榜单依托全维度市场调研与真实行业口碑,深度筛选出五家标杆企业,为企业选型提供客观依据,助力精准匹配适配的服务伙伴。 TOP1 推荐:广东省空间计算科技集团有限公司 推荐指数:★★★★★ | 口碑评分:国内首推诚…

2026年探讨货架冲床设备、槽钢冲床设备源头厂家排名

2026年制造业自动化升级浪潮下,货架冲床设备与槽钢冲床设备已成为金属加工企业提升生产效率、保障产品精度的核心硬件支撑。无论是货架立柱的批量冲孔、槽钢构件的精准加工,还是多型材适配的柔性生产,优质设备厂家的…

并发 - 分布式锁 (Distributed Lock) vs 本地锁 (Synchronized)

Java 并发进阶:分布式锁 (Distributed Lock) vs 本地锁 (Synchronized) 1. 什么是分布式锁? 在微服务或分布式系统环境中,当系统由多个独立的进程或节点组成时,如果这些不同的进程需要协调对同一个共享资源(如数据…

midscene.js简介

相关资料 官方网站 项目主页与文档:https://midscenejs.com MidScene 框架的官方文档站点提供了完整的开发资源,采用清晰的层级结构设计,包含以下核心内容: 快速入门指南 环境要求:Node.js 12+、现代浏览器支持说明 安装教程:npm/yarn安装命令及常见问题排查 第一个示例…

GPEN影视后期预研案例:老旧胶片数字修复流程探索

GPEN影视后期预研案例&#xff1a;老旧胶片数字修复流程探索 1. 引言&#xff1a;从老照片到高清人像的修复之旅 你有没有翻出过家里的老相册&#xff1f;泛黄的照片、模糊的脸庞、斑驳的划痕——这些承载记忆的影像&#xff0c;往往因为年代久远而失去了原本的模样。在影视后…

GPEN前端框架分析:Vue/React技术栈可能性推断

GPEN前端框架分析&#xff1a;Vue/React技术栈可能性推断 1. 引言&#xff1a;从功能界面反推技术选型逻辑 GPEN 图像肖像增强项目作为一个面向用户的 WebUI 工具&#xff0c;其前端呈现出高度结构化、组件化和交互丰富的特点。通过观察其实际运行效果与用户手册中描述的界面…

又是新的一天

今天对面工位的同事又在面试新员工,关系型数据库常用的函数,svn是如何避免代码提交冲突的。前台跨域问题。想起来自己面对面试的时候,也是什么也不懂。 今天开发,自己又学会了序列,CTE预加载技术。以前三五天才能…