跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
Javajava算法

Java 并发三大特性:原子性、可见性与有序性详解

Java 并发编程的核心在于原子性、可见性和有序性。原子性指操作不可分割,常用锁或 CAS 保证;可见性解决缓存一致性问题,volatile 和 synchronized 可确保数据实时同步;有序性防止指令重排导致的逻辑错误,JMM 通过 happens-before 规则约束。理解这些机制有助于编写正确的多线程代码,避免常见的并发 Bug。

王者发布于 2026/3/21更新于 2026/5/33 浏览
Java 并发三大特性:原子性、可见性与有序性详解

Java 并发编程中,原子性、可见性和有序性是三个核心概念。理解它们对于排查多线程 Bug 至关重要,绝大多数并发问题都能归因于这三者之一。

并发问题示意图

原子性

原子性意味着一个操作要么全部执行完成,要么完全不执行,中间不会被其他线程打断。以 i++ 为例,这行代码在底层其实是三步操作:读取变量值、加 1、写回内存。在多线程环境下,如果没有同步机制,多个线程可能同时读取到同一个旧值,导致最终结果不正确。

可见性

可见性是指当一个线程修改了共享变量,其他线程能够立即感知到这个变化。现代 CPU 拥有多级缓存(L1/L2/L3),线程通常优先读写自己的缓存。如果线程 A 修改了变量但数据还停留在本地缓存未刷回主内存,线程 B 读取到的就是旧值。

有序性

有序性要求程序执行的顺序与代码编写的顺序一致。为了优化性能,编译器和 CPU 会对指令进行重排序。单线程下这种重排序不会影响结果(as-if-serial 语义),但在多线程环境中,指令乱序可能导致逻辑错误。

下面通过双重检查锁定(DCL)的单例模式示例,看看这三个问题如何引发 Bug:

public class Singleton {
    private static Singleton instance;

    public static Singleton getInstance() {
        if (instance == null) { // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) { // 第二次检查
                    instance = new Singleton(); // 潜在问题点
                }
            }
        }
        return instance;
    }
}

这段代码看起来没问题,但 instance = new Singleton() 实际上分三步:分配内存空间、初始化对象、将引用指向内存地址。CPU 或编译器可能将第 2 步和第 3 步重排序。如果发生重排序,另一个线程可能在对象未完成初始化时就获取到了引用,导致空指针异常或数据错乱。解决方案是给 instance 加上 volatile 关键字。

扩展知识

原子性的保障手段

Java 保证原子性主要靠锁和 CAS(Compare-And-Swap)。

synchronized 和 Lock 是最直接的手段,进入临界区的线程独占资源,其他线程阻塞等待。虽然简单可靠,但锁的开销较大,涉及线程切换和上下文保存。

CAS 是一种乐观锁策略,依赖 CPU 的 指令。例如 的 ,它会不断尝试比较当前值是否等于预期值,相等则更新,失败则重试。CAS 避免了线程阻塞,但在高竞争场景下会导致自旋消耗 CPU 资源。

cmpxchg
AtomicInteger
incrementAndGet

JDK 8 引入了 LongAdder,它将变量拆分为多个 Cell,不同线程操作不同的 Cell,最后汇总。在高并发计数场景下,它的性能远优于 AtomicLong,Elasticsearch 的计数器就采用了类似方案。

LongAdder 原理图

可见性的底层原理

可见性问题根源在于 CPU 缓存一致性。volatile 的作用就是强制刷新缓存。写入 volatile 变量时,JVM 会插入 StoreStore 和 StoreLoad 屏障,确保数据刷回主内存;读取时插入 LoadLoad 和 LoadStore 屏障,强制从主内存加载。

synchronized 也能保证可见性。线程退出 synchronized 块时,会将所有修改刷回主内存;进入时清空本地缓存,强制重新加载。因此 synchronized 块内的代码无需额外加 volatile。

此外,final 字段也有可见性保证。JVM 保证对象构造完成后,final 字段的值对其他线程可见,这也是 String 的 value 数组被声明为 final 的原因。

有序性与指令重排

编译器和 CPU 都会进行指令重排序以提升效率。编译器调整字节码顺序以优化寄存器使用;CPU 则利用流水线并行执行无依赖指令,执行完再按原始顺序提交结果。

单线程下完全安全,因为 CPU 保证了 as-if-serial 语义。但在多线程中,A 线程认为无依赖的指令,对 B 线程可能有依赖。JMM 定义了 happens-before 规则来约束重排序:只要操作 A happens-before 操作 B,那么 A 的结果对 B 一定可见,且顺序在前。

三大特性的实现方式对比

特性volatilesynchronizedLockAtomic
原子性不保证保证保证保证
可见性保证保证保证保证
有序性禁止重排序临界区内有序临界区内有序单个操作有序
性能最轻中等可控较轻
适用场景状态标记临界区保护精细控制计数器

面试官追问

Q: volatile 能保证原子性吗?为什么 volatile int count 的 count++ 不是线程安全的?

A: volatile 只保证可见性和禁止重排序,不保证原子性。count++ 实际上是读取、加 1、写回三个步骤,多个线程可能同时读到同一个值,各自加 1 后写回,导致结果少加。想要原子自增得用 AtomicInteger 或者 synchronized。

Q: synchronized 和 volatile 在底层实现上有什么区别?

A: volatile 是通过内存屏障实现的,纯粹靠 CPU 指令保证,不涉及锁。synchronized 底层是 monitor 机制,JVM 会在对象头里记录锁状态,涉及偏向锁、轻量级锁、重量级锁的升级过程,重量级锁依赖操作系统互斥量,有线程切换开销。

Q: 为什么双重检查锁定的单例需要加 volatile,不加会出什么问题?

A: new 对象分三步:分配内存、初始化、引用赋值。不加 volatile 的话,CPU 可能把初始化和引用赋值重排序,另一个线程可能在第一次 null 检查时拿到一个非 null 但还没初始化完的引用,直接用就空指针或者数据错乱。volatile 禁止了这种重排序。

目录

  1. 原子性
  2. 可见性
  3. 有序性
  4. 扩展知识
  5. 原子性的保障手段
  6. 可见性的底层原理
  7. 有序性与指令重排
  8. 三大特性的实现方式对比
  9. 面试官追问
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 利用 Python 爬虫进行跨境电商数据采集
  • 对比 Copilot 与国产 AI 编程助手:通义灵码、Comate 深度评测
  • Linux 基础 IO(三):文件描述符与重定向
  • 使用 Whisper 构建企业级离线语音转录系统
  • AutoGPT 结合 Python 实现 AI 智能体自动化任务指南
  • Figma Make 实测:AI 驱动的前端代码生成能力解析
  • 基于 LLaMA-Factory 微调 Qwen-0.6B 模型至高通 NPU 部署指南
  • Flet:用 Python 构建跨平台桌面与 Web 应用
  • Ultrascale 架构 FPGA System Management Wizard 温压监测配置
  • Transformer 入门到精通:核心架构与算法详解
  • C++ 内存模型与原子操作详解
  • Java 数据结构:栈与队列核心指南
  • AI 生成图片提示词:新手入门指南与最佳实践
  • 数据结构入门:基于数组的栈结构实现详解
  • iOS 26 系统兼容适配:UITabBar 液态玻璃效果与 WiFi SSID 获取
  • 使用 PyTorch 从零构建 Transformer 模型:原理、代码与训练预测
  • AI 绘画提示词引导系数设置指南:从原理到实践
  • 医疗编程AI技能树与培训体系分析:国内外一流大学专业解读
  • 医疗 AI 轻量化部署方案梳理与优化路径
  • Vue 组件开发中的枚举值验证:从 Type 属性错误说起

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online