volatile不能替代加锁实现状态机切换,因其仅保证可见性和禁止重排序,无法保障“读取-判断-写入”复合操作的原子性;需用atomicreference配合cas实现线程安全的状态跃迁。

volatile 不能替代加锁实现状态机切换,它只能保证变量的可见性和禁止指令重排序,但无法保证复合操作的原子性。状态机切换通常涉及“读取当前状态 → 判断条件 → 写入新状态”这一系列步骤,这属于典型的 检查-执行(check-then-act) 操作,volatile 无法确保整个过程不被其他线程干扰。
为什么 volatile 不足以支撑状态机切换
例如一个三态机:INIT → RUNNING → STOPPED,若用 volatile 字段表示状态:
看似安全,但以下代码仍是线程不安全的:
if (currentState == INIT) {currentState = RUNNING; // 非原子:读+写两步,中间可能被抢占
}
两个线程同时看到 INIT,都执行赋值,结果可能跳过校验逻辑,甚至违反状态流转规则(如从 STOPPED 直接切回 RUNNING)。
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。 当用户需要:(1) 编写或审查Java代码 (2) 检查命名/代码规范 (3) 处理异常和日志 (4) 编写单元测试 (5) 安全编码 (6) 数据库设...
真正可行的轻量级替代方案:AtomicReference + CAS
用 AtomicReference<state></state> 替代 volatile 字段,配合 compareAndSet 实现带条件的状态跃迁:
- 每次状态变更前,先读取当前值,再通过 CAS 原子地判断并更新
- 失败时可重试、抛异常或返回 false,逻辑可控
- 无需阻塞,无锁但线程安全
public boolean start() {
return state.compareAndSet(INIT, RUNNING) ||
state.compareAndSet(STOPPED, RUNNING); // 允许从 STOPPED 重启
}
更严谨的做法:封装状态流转逻辑
把合法转移路径显式建模,避免散落的 CAS 判断:
- 定义
canTransitionFromTo(from, to)方法校验是否允许该跳转 - 在
setState中统一用 CAS 尝试更新,并仅当校验通过时才提交 - 必要时配合
getAndSet或updateAndGet实现更复杂语义(如计数器式状态)
什么时候可以只用 volatile
仅限于满足以下全部条件的极简场景:
- 状态只有单向变化(如
UNINITIALIZED → INITIALIZED),且永不回退 - 状态变更由单一写线程完成,其他线程只读不干预流程
- 不依赖“读-改-写”逻辑,只是发布最终结果(类似双重检查锁定中的 instance 字段)
这种情况下 volatile 足够,但它已不是“状态机切换”,而是“状态发布”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










