最近看volatile和AtomicBoolean,有时候搞不懂到时啥时候使用,因此问了AI,做一下记录,方便自己查阅。
正文
什么是volatile?
volatile 是一个关键字,修饰变量,干两件事:
可见性:一个线程改了值,另一个线程立刻能看到,不会被 CPU 缓存糊弄
禁止指令重排:写操作前的代码不会被重排到写操作后(配合 happens-before 规则)
但它不保证原子性!
volatile boolean flag = false; // 线程 A flag = true; // 单步写操作,没问题 // 线程 B if (flag) { // 能看到最新值 ... }
这个场景完全够用。但下面这种就不行了:
volatile int count = 0; // 多线程同时执行 count++; // 读-改-写三步操作,volatile 保护不了
什么是AtomicBoolean?
AtomicBoolean 是一个类,内部用 volatile boolean + CAS(Compare And Swap) 指令实现的。
private volatile int value; // 内部实现,用 int 存 0/1它提供的不是"可见性"这么简单,而是整个操作一步到位:
AtomicBoolean started = new AtomicBoolean(false); // 只有第一个线程能成功,其他线程直接返回 false if (started.compareAndSet(false, true)) { // 初始化逻辑,只会执行一次 }
compareAndSet 是原子的——读、比较、写三步在 CPU 层面是一条指令,中间不会被打断。
纯状态标记,且只有一个线程写,多个线程读:
volatile boolean isShuttingDown = false; // 多个工作线程 while (!isShuttingDown) { doWork(); } // 管理线程 isShuttingDown = true;
或者你只做单次赋值:
volatile BluetoothDevice bondedDevice; bondedDevice = device; // 直接写,不需要原子性
用 AtomicBoolean的情况
需要"检查再修改",而且可能有多个线程同时竞争:
// 只初始化一次的 RFCOMM 服务 AtomicBoolean rfcommInitialized = new AtomicBoolean(false); void initRfcomm() { if (rfcommInitialized.compareAndSet(false, true)) { // 这里只会有一个线程进来 setupRfcommSocket(); } }
再比如你之前那个 AA 连接流程里的状态机:
enum ConnectionState { IDLE, PAIRING, CONNECTING, CONNECTED } AtomicReference<ConnectionState> state = new AtomicReference<>(ConnectionState.IDLE); // 只有当前是 IDLE 才能切到 PAIRING if (state.compareAndSet(IDLE, PAIRING)) { startPairing(); }
对比
| 特性 | volatile | AtomicBoolean |
|---|---|---|
| 可见性 | 保证 | 保证 |
| 有序性 | 禁止指令重排 | 禁止指令重排 |
| 原子性 | 不保证 | 保证 (CAS 机制) |
| 底层实现 | CPU 内存屏障 | CAS (Compare-And-Swap) |
| 性能开销 | 极低 | 较低 (无锁,但有 CAS 开销) |
| 适用场景 | 状态标志位、DCL 单例 | 并发计数器、开关控制、状态机 |
性能上:
volatile几乎没有额外开销,就是内存屏障AtomicBoolean在高并发下 CAS 失败会自旋重试,竞争激烈时可能浪费 CPU
但在 Android 车载这种并发量不高、但正确性要求高的场景,AtomicBoolean 用起来比你自己加 synchronized 轻量多了。
小结
volatile 保证可见性和有序性,但不保证原子性;AtomicBoolean 在 volatile 的基础上额外保证了操作的原子性。

