Skip to content

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

  • 标签:Java并发、Java

🏛 业务架构师 ​

并发编程的三性是 Java 内存模型(JMM)定义的问题域:

  • 原子性:操作不可分割。count++ 实际是读-改-写三步,并发下丢更新——业务后果:库存超卖、计数不准、重复发号;
  • 可见性:一个线程的修改对其他线程立即可见。CPU 缓存与编译器优化让线程各自看到旧值——业务后果:一个线程把订单状态改成"已取消",另一线程还在按"进行中"处理,双份动作;
  • 有序性:程序按预期顺序执行。指令重排在单线程无感(as-if-serial),多线程下交错暴露——经典如双重检查锁单例没加 volatile 时"拿到半初始化对象"。

对应的工具映射:synchronized/volatile(可见性与有序)、CAS 与锁(原子性)、happens-before 规则(JMM 给的推理依据)。

业务架构视角我想讲三性问题的真正代价:并发 bug 具有**"测试环境复现不了、生产偶发、出现即资损"的恶性质——压测百万次不出错、大促当天超卖 200 单。所以我的治理策略是降低出错面而不是依赖程序员正确**:

  1. 优先不可变:全 final 对象 + 原子替换引用,三性问题天然免疫;
  2. 优先现成容器与工具:ConcurrentHashMap、AtomicLong、并发队列——JDK 已经把正确性造好了;
  3. 共享可变状态是设计坏味道:评审时追问"这个字段为什么被多线程写?能不能收口到单线程(队列串行化、Actor 模式)"——用架构消除并发,而不是用技巧驯服并发。

一句话:三性是知识,"能不共享就不共享、能不可变就不可变"是纪律。

🏗 系统架构师 ​

并发编程的三座大山,对应三个底层问题来源:

  • 原子性:操作不可分割。i++ 实际是读-改-写三步,并发交错导致丢失更新。来源:CPU 时间片切换发生在指令序列中间。解法:synchronized/ Lock(互斥)、CAS + Atomic 类(无锁原子指令)、LongAdder(分段降低竞争);
  • 可见性:一个线程的修改另一线程看不到。来源:CPU 多级缓存 + 编译器优化,每个核先写自己的 cache/write buffer。解法:volatile(写立即刷主存、读强制失效,配合 MESI 协议)、synchronized 的解锁前刷写、final 的构造语义;
  • 有序性:代码执行顺序与书写不一致。来源:编译器指令重排 + CPU 乱序执行,单线程结果不变即可重排(as-if-serial),多线程下重排破坏他线程假设(经典 DCL 半初始化对象)。解法:volatile 禁止重排(内存屏障)、happens-before 规则推演。

串起三者的纲领是 JMM / happens-before:程序顺序、监视器锁、volatile、线程 start/join 等规则给出"一个写对另一个读可见"的推导链。我的实践方法论:先划清共享可变状态,能不可变则不可变(final/快照),必须可变则缩小临界区(锁)或用 CAS,再按 happens-before 校验发布关系——并发设计做到"有依据"而不是"碰运气"。

💻 全栈开发专家 ​

并发三大问题 + 各自来源和 Java 对策:

  • 原子性:一个操作不可分。i++ 是读-改-写三步,并发会丢更新。来源:指令级非原子。对策:synchronized、Lock、CAS(Atomic 类)、LongAdder。
  • 可见性:A 改了共享变量,B 立刻看到吗?CPU 多级缓存 + 写未刷主内存导致脏读。对策:volatile(写刷主存 + 读失效缓存行)、synchronized(解锁前刷写)、final(安全发布)。
  • 有序性:编译器/CPU 指令重排让代码执行顺序变化,单线程 as-if-serial 无感,多线程出问题。对策:volatile 内存屏障、synchronized 互斥。

JMM 的角色:把"物理真相"抽象成主内存 + 各线程工作内存模型,用 happens-before 规则给出可推理的语义(程序次序、锁的解锁先于加锁、volatile 写先于读、线程 start/join 等),符合 happens-before 的操作不需要额外同步。

经典案例(我拿这个串三性)——双检锁单例:

java
private static volatile Singleton instance;  // 没有 volatile 会怎样?

new Singleton() 是三步:分配内存 → 初始化 → 引用赋值。2、3 步可能重排,另一线程拿到"未初始化完的引用"(有序性问题),字段还可能是 0/null(可见性问题)。一个 volatile 同时解决两者——这就是"懂 JMM"和"背题"的区别。