Java中除long、double外的基本类型单次读/写是原子的,但i++等复合操作因含读-改-写三步且无内存屏障,必然非原子;volatile不能修复其原子性缺陷,须用AtomicInteger或锁机制保障。

Java基本数据类型在多线程下的原子性,不能一概而论——它取决于操作类型、数据宽度和JVM实现细节。简单赋值是原子的,但复合操作(如自增)一定不是;long/double在32位环境尤其需警惕。
哪些读写操作天然原子
对除 long 和 double 外的所有基本类型(int、short、char、byte、boolean),单次读或单次写是JVM保证的原子操作:
-
int x = 5;—— 一次写入,原子 -
boolean flag = true;—— 一次写入,原子 -
char c = 'a';—— 一次写入,原子 -
int y = x;—— 注意:这其实是“读x + 写y”两步,整体不原子;只有“读x”或“写y”各自算原子子操作
为什么 long 和 double 是例外
在32位JVM上,64位的 long 和 double 可能被拆成两个32位操作(高位+低位)。若线程A刚写完低位、还没写高位时被中断,线程B读到的就是“高低位不匹配”的脏值(如高位是旧值、低位是新值)。
解决办法有二:
立即学习“Java免费学习笔记(深入)”;
- 用 volatile 修饰(强制按原子方式读写)
- 改用 AtomicLong / AtomicDouble(底层用CAS保障)
i++ 这类操作为何一定不原子
表面一行代码,实际包含三个独立、可被中断的步骤:
- 从主内存或线程本地副本中读取当前值
- 在CPU寄存器中执行加1运算
- 将结果写回变量所在内存位置
两个线程同时执行 i++(假设i初始为5)可能产生竞态:都读到5 → 都算出6 → 都写回6 → 最终i=6而非7。这不是“慢”,而是逻辑断裂。
如何安全实现原子更新
不能依赖基本类型的“天然原子性”来保障业务逻辑正确性。推荐方式:
- 用 AtomicInteger 等原子类:
counter.incrementAndGet()(底层CAS+自旋,无锁高效) - 用 synchronized 块或方法:适合复杂逻辑,语义清晰,JVM优化成熟
- 用 ReentrantLock:需要可中断、超时等高级控制时选用
- 避免仅靠 volatile 修复 i++ —— 它只保可见性和有序性,不保原子性


















