Java什么是原子性
原子性(Atomicity)就是指一个操作要么一次性做完,要么什么都不做,中间不允许被打断、分割,像“原子”一样嘛!一句话来说不可分割,不可终中断
比如你去银行取钱,银行从卡里扣100块钱,同时把这100块交给你,这两个步骤必须是“原子的”,要么一起成功要么什么都不发生,如果中间突然被打断(只扣钱没给你现金),那指定不行呀。
在Java并发里,原子性指什么?
在多线程里,原子性意味着这个操作在CPU或JVM看来是“一条指令”,不会被别的线程打断、插入、看到一半的中间状态。
场景:多线程并发
在 Java 多线程里,多个线程可能同时对同一个变量/内存地址做读写操作。
如果这个操作不是原子的,就可能出现:线程 A 做到一半被挂起--》线程 B 进来改了值--》线程 A 恢复继续执行。这样就会有“线程安全”问题,比如经典的 ++ 操作。
什么是“对 CPU/JVM 来说是一条指令”? i++ 操作在 JVM 或 CPU 里其实不是“一步”,底层包含3步
1. 读取 i 的值
2. i + 1
3. 把新值写回去
如果两个线程同时对 i++
,有可能:
线程 A 读到
i=5
线程 B 也读到
i=5
A +1 后写回去,i = 6
B +1 后也写回去,i 还是 6(丢了一次加 1)
结果少加了!这就是非原子操作的典型并发 BUG。
怎么保证原子性?
Java的第一招:synchronized;用锁把一段代码包起来,同一时间只能一个线程执行,原子性 + 可见性都保证了。
第二招volatile:注意!volatile
只能保证可见性 & 禁止重排序,但不保证原子性。所以 volatile
的 count++
还是不安全。
第三招原子类(AtomicInteger
、AtomicBoolean
):Java 提供了 java.util.concurrent.atomic
包,比如 AtomicInteger
。它底层用了 CAS(Compare And Swap)原语,保证对某个变量的读改写是原子的。