什么是 Java 中的原子性、可见性和有序性?

Java 中的原子性、可见性、有序性 是多线程编程中最核心的三个特性,也称为“并发三要素”或“happens-before 关系的三大保证”。

它们直接决定了代码在多线程环境下是否能得到“预期”的执行结果。下面用最直白的方式解释三者的含义、为什么会丢失、以及 Java 是如何(或没能)保证它们的。

1. 原子性(Atomicity)

定义
一个操作(或一组操作)要么全部执行成功,要么全部不执行,不会出现“执行到一半”的中间状态。

最经典的非原子例子(很多人踩过的坑):

int count =0;// 线程 A 线程 B count++; count++;

上面看似一行代码,实际 JVM 会分解为:

  1. 读取 count 的值
  2. +1
  3. 把新值写回 count

所以真正的执行序列可能是:

线程A 读 0 线程B 读 0 线程A +1 → 1 线程A 写回 1 线程B +1 → 1 线程B 写回 1 

最终 count = 1,而不是预期的 2 → 丢失更新(非原子)

Java 中真正具有原子性的操作(部分):

操作类型是否原子说明
基本类型(除 long/double 外)的读写32位以内基本类型读写是原子的
long / double 的读写不是(JDK8 前)64位拆成两个 32 位,可能看到“半写”
volatile long / double是(JDK5+)volatile 保证 long/double 读写原子
使用 AtomicXXX 类AtomicInteger、AtomicLong 等
synchronized / Lock可以做到保护的代码块整体原子

小结
普通 int 的 count++不是 原子操作
想保证原子性 → 用 AtomicIntegersynchronizedReentrantLockLongAdder


2. 可见性(Visibility)

定义
当一个线程修改了共享变量的值,其他线程立刻能看到最新的值。

经典反例(最常出现的可见性问题):

boolean running =true;voidstop(){ running =false;// 线程A执行}voidrun(){while(running){// 线程B一直循环// do something}}

很多时候线程B 永远停不下来,因为它读到的 running 一直是自己工作内存里的旧值(true),没有从主内存刷新最新值(false)。

原因
Java 内存模型(JMM)允许线程把变量先写到本地工作内存(CPU 缓存),而不是立刻写回主内存。
其他线程可能长时间从自己的缓存读旧值。

Java 提供可见性保证的方式

方式是否提供可见性额外效果典型使用场景
volatile禁止指令重排序状态标志、单次发布
synchronized进入/退出同步块刷新保护共享变量的复合操作
Lock(ReentrantLock)同 synchronized需要可中断、超时、公平锁场景
final是(初始化后)禁止重排序不可变对象、安全发布
AtomicXXXCAS + volatile 语义原子操作 + 可见性

记住一句话
只要变量被 volatile 修饰、或者在 synchronized 块内读写、或者通过 Atomic 类操作,就有了可见性保证。


3. 有序性(Ordering)

定义
代码的执行顺序 不一定 等于程序代码的书写顺序(只要不影响单线程结果,编译器/JVM/CPU 都可以重排序)。

经典反例(双重检查锁单例的陷阱):

classSingleton{privatestaticSingleton instance;// 非 volatilepublicstaticSingletongetInstance(){if(instance ==null){// 第一次检查synchronized(Singleton.class){if(instance ==null){// 第二次检查 instance =newSingleton();// 问题在这里!}}}return instance;}}

new Singleton() 不是一个原子操作,它有三个步骤:

  1. 分配内存
  2. 初始化对象(调用构造方法)
  3. 把引用指向这块内存(instance 指向新对象)

指令重排序后可能出现

线程 A:1→3→2(先把引用指向内存,再初始化)
线程 B:看到 instance != null,直接返回 → 拿到半初始化对象 → 使用时崩溃或逻辑错误

解决方案(三种主流写法):

// 写法1:volatile(最常用、最清晰)privatestaticvolatileSingleton instance;// 写法2:静态内部类(推荐,无 volatile 也安全)privatestaticclassHolder{privatestaticfinalSingleton INSTANCE =newSingleton();}publicstaticSingletongetInstance(){returnHolder.INSTANCE;}// 写法3:枚举(最安全、最简洁)publicenumSingleton{ INSTANCE;// 其他方法...}

Java 提供有序性保证的方式

方式禁止的重排序类型典型使用场景
volatile写→读、写→写DCL 单例、状态标志
synchronized进入/退出同步块前后任何需要同步的代码块
finalfinal 字段初始化 → 后续读不可变对象、安全发布
Happens-before 规则多种情况(详见 JMM 8 条规则)理解并发语义的核心

快速记忆对比表(面试/实战最常用)

特性关注点丢失的表现常见解决方案典型工具/关键字
原子性操作不可分割读-改-写中间被打断synchronized / Lock / AtomicXXXAtomicInteger、LongAdder
可见性写后其他线程能看到一个线程改了值,另一个线程看不到volatile / synchronized / final / Lockvolatile
有序性代码不按书写顺序执行new 对象引用先发布,构造未完成volatile / final / synchronizedvolatile + DCL、静态内部类

一句话总结(最常考的表述):

Java 并发编程的三大核心问题:原子性:操作要“要么全做,要么不做”可见性:修改要“让别人看得到”有序性:不要“看起来合理的重排序”把程序搞乱

如果你想继续深入某个点(比如 happens-before 规则完整列表、volatile 底层内存屏障、JMM 模型图解、LongAdder 原理等),可以直接告诉我,我再给你展开更详细的例子和代码。

Read more

吃透「哈希表」核心优化思路,告别 O (n²) 性能黑洞

吃透「哈希表」核心优化思路,告别 O (n²) 性能黑洞

哈希表(HashMap/HashSet/ 字典)是程序员最常用的数据结构之一,更是解决性能瓶颈的「黄金工具」。日常开发中,我们处理的海量数据匹配、元素查找、去重统计等场景,若用数组遍历、双层循环实现,时间复杂度往往是 O (n²),数据量一旦过万就会出现明显卡顿;而基于哈希表的核心特性 ——平均 O (1) 的查询与插入效率,能轻松将这类场景的性能提升百倍甚至千倍。 但很多程序员对哈希表的认知停留在「会用就行」,只会无脑 new HashMap (),却忽视了哈希冲突、内存浪费、选型不当等问题,导致代码看似用了高性能结构,实际性能大打折扣,甚至埋下线上隐患。本文结合 Java、Python、Go 等主流语言的实战场景,拆解哈希表的核心原理、避坑指南与极致优化思路,让你真正用好这个性能利器。 一、为什么哈希表能成为「性能救星」?核心底层逻辑 哈希表的核心优势,源于其「空间换时间」的设计思想,

By Ne0inhk
【Java】从树形结构到二叉树:一篇搞懂数据结构里的“家族树”

【Java】从树形结构到二叉树:一篇搞懂数据结构里的“家族树”

🎁个人主页:User_芊芊君子 🎉欢迎大家点赞👍评论📝收藏⭐文章 🔍系列专栏:Java.数据结构 【前言】 你有没有想过,电脑里的文件分类、通讯录的层级关系,其实都藏着“树”的影子?树形结构是数据结构里最像“现实家族关系”的存在,而二叉树更是其中的“明星选手”——它规则清晰、操作灵活,是很多复杂数据处理的基础。这篇文章会从树形结构的概念入手,一步步拆解二叉树的类型、性质、存储和操作,帮你把这些抽象的结构变成能上手用的知识~ 文章目录: * 一、树形结构 * 1.树形结构的概念 * 2.树的表示形式 * 二、二叉树 * 1.概念 * 2.二叉树类型 * 2.1 满二叉树 * 2.2 完全二叉树 * 3.

By Ne0inhk
Flutter 三方库 collection 的鸿蒙化适配指南 - 实现具备高级集合操作与相等性深度判定算法的算法底座、支持端侧多维数据结构的高性能治理实战

Flutter 三方库 collection 的鸿蒙化适配指南 - 实现具备高级集合操作与相等性深度判定算法的算法底座、支持端侧多维数据结构的高性能治理实战

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 collection 的鸿蒙化适配指南 - 实现具备高级集合操作与相等性深度判定算法的算法底座、支持端侧多维数据结构的高性能治理实战 前言 在进行 Flutter for OpenHarmony 开发时,面对复杂的业务 JSON 转化、深层嵌套的集合对比或需要对列表执行高频的优先级排序(Priority Queue)时,原生 List 和 Map 的功能往往显得捉襟见肘。collection 是 Dart 官方维护的最权威、最核心的集合工具库。本文将探讨如何在鸿蒙端构建极致、稳健的数据处理架构。 一、原直观解析 / 概念介绍 1.1 基础原理 该库扩展了 Dart 标准库中的集合能力。它不仅提供了如 Equality(深度相等判定)、PriorityQueue(

By Ne0inhk
Flutter 组件 humanize 的适配 鸿蒙Harmony 深度进阶 - 驾驭多语言复数逻辑算法、实现鸿蒙端中式大额单位感知与极致人性化文本渲染方案

Flutter 组件 humanize 的适配 鸿蒙Harmony 深度进阶 - 驾驭多语言复数逻辑算法、实现鸿蒙端中式大额单位感知与极致人性化文本渲染方案

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 humanize 的适配 鸿蒙Harmony 深度进阶 - 驾驭多语言复数逻辑算法、实现鸿蒙端中式大额单位感知与极致人性化文本渲染方案 前言 在前文我们掌握了 humanize 进行基础数据转换的方法。但在鸿蒙(OpenHarmony)面向全球市场的布局中,真正的技术挑战往往隐藏在极其琐碎的“语言表达”中。例如:在英文中我们说 1 items 是错误的,必须是 1 item 与 2 items;而在中文环境下,我们虽然没有复数形变,但却有“万、亿”这类独特的四位一级计数逻辑。 一个真正具备“高级感”的鸿蒙应用,不应在数据展示上显得僵硬且带有明显的机器翻译痕迹。 本文将作为 humanize 适配的进阶篇,带你攻克多语言复数(Pluralization)

By Ne0inhk