原子性指单条指令不可分割执行,如int基本类型单次读写;i++等复合操作非原子,需synchronized或AtomicInteger;volatile仅保可见性与有序性,不保原子性。

原子性不是“看起来像一步”,而是“真的一条指令完成”
很多人以为 i++ 是原子操作,其实它在 JVM 层面拆成三步:read → add → write,线程切换可能卡在任意中间点。比如两个线程同时对 count = 0 执行 100 次 count++,最终结果大概率小于 200。
- 真正原子的操作只有:基本类型(
int、boolean等)的**单次读或单次写**(如a = 5),但a = b或a++都不算 - 想保证复合操作原子性,必须用显式同步:要么加
synchronized块锁住临界区,要么用AtomicInteger的incrementAndGet()——它底层靠 CPU 的CAS指令实现,不是“模拟原子”,是硬件级原子 - 注意陷阱:
AtomicInteger能保单个变量操作原子,但多个原子变量组合(如先atomicA.get()再atomicB.set(...))依然不原子,没锁住整个逻辑段
有序性被打破不是 bug,是 JVM 和 CPU 的“性能优化特权”
你写的代码顺序是 a = 1; flag = true;,但 JVM 可能重排成 flag = true; a = 1;——只要单线程结果不变,就合法。可一旦 flag 是线程间通信信号,另一线程看到 flag == true 却读到 a == 0,就出问题了。
-
volatile是最轻量的有序性保障:它禁止其前后指令与它发生重排序(即插入memory barrier),但仅限于该变量自身读写,不约束其他变量 -
synchronized和Lock不仅保原子,也天然保有序:进入/退出同步块时有隐式内存屏障,块内所有操作不会被重排到块外 - 别依赖“代码写在前面就一定先执行”——没有同步机制时,JVM、编译器、CPU 都可能动它;happens-before 规则才是判断执行顺序的唯一依据
原子性和有序性经常被一起破坏,但修复手段不能混用
比如用 volatile int counter 试图解决 counter++ 的线程安全问题:它能保证其他线程看到最新值(可见性),也能禁止部分重排(有序性),但**完全无法阻止 read-modify-write 中间被插入其他线程操作**——所以结果依然错。
-
volatile≠ 原子性:它只管“读写立刻刷主存+禁止重排”,不管“操作是否可分割” -
AtomicInteger同时解决原子性和有序性:它的getAndIncrement()是一个 CAS 循环,每次尝试都包含“读当前值→比对→写新值”整套动作,失败就重试,且整个过程有内存屏障保证顺序 - 常见误配:给
double或对象引用加volatile就以为线程安全了——如果要更新对象内部字段,还得靠AtomicReference或锁
真正难的不是记住定义,而是看代码时能一眼识别出哪行在多线程下会“裂开”:是不是读-改-写?有没有跨线程依赖的执行顺序?用了 volatile 却还在做非原子计算?这些地方不加验证,光靠“应该没问题”上线,早晚遇到偶发的计数偏差或状态错乱。
立即学习“Java免费学习笔记(深入)”;










