发布于2026-05-21 阅读(0)
扫一扫,手机访问
说到并发编程里的状态管理,很多开发者第一反应可能是加锁。但如果你仔细琢磨一下,其实很多场景下,状态本身就是一个简单的整型值,比如初始化、运行中、已暂停。这时候,AtomicInteger 就能派上大用场——它远不止是个计数器,更是一个实现轻量级、无锁状态机的绝佳选择。

它的适用场景很明确:状态用整数表示、转换逻辑相对简单、并且每次状态变更只需要一次“检查-更新”操作。满足这几点,用 AtomicInteger 来实现,代码会非常简洁高效。
第一步,是把业务状态清晰地映射为整数。比如,0 代表初始化(INIT),1 代表运行中(RUNNING),以此类推。这里的关键在于,状态值必须互斥,不能有重叠。千万别想着用一个整数的不同位来同时表示多个标志位——那是 AtomicIntegerFieldUpdater 或者直接用 volatile int 加锁的领域,别混为一谈。
在实际操作中,有几点经验之谈:
public static final int 这样的常量来明确定义每个状态,彻底告别代码里的“魔法数字”。Long 类型或者其他独立字段该干的事。实现状态转换,最忌讳的就是先 get()、再判断、最后 set() 这种“三步走”。这在并发下根本不是原子的。正确的姿势是,直接使用 compareAndSet(expect, update) 这个原子操作。
举个例子,如果只允许从 INIT 状态转换到 RUNNING 状态,并且要防止重复启动,代码应该这样写:
if (status.compareAndSet(INIT, RUNNING)) {
// 转换成功:执行对应的初始化逻辑
doInitialize();
} else {
// 转换失败:说明当前已经不是INIT状态了,可能被其他线程抢先了
throw new IllegalStateException("Invalid state transition");
}
看到没?整个 if 块是线程安全的。compareAndSet 是一条 CPU 指令级别的原子操作,根本不会出现那种“我读的时候是 INIT,但准备写的时候已经被别人改成 RUNNING 了”的尴尬竞态。
现实情况往往更复杂。比如,STOPPED(停止)状态可能允许从 RUNNING 或者 PAUSED(暂停)状态进入。这时候,一次 compareAndSet 就不够了,需要引入循环尝试机制,也就是常说的 CAS 自旋。
int current;
do {
current = status.get();
if (current == RUNNING || current == PAUSED) {
if (status.compareAndSet(current, STOPPED)) {
break; // 转换成功,跳出循环
}
// 如果CAS失败,说明current值在获取后又被其他线程改了,循环重试
} else {
throw new IllegalStateException("Cannot stop from state: " + current);
}
} while (true);
这种结构适用于状态转换条件不多、且并发冲突不频繁的场景。如果状态分支非常复杂,或者状态转换过程中还涉及外部I/O操作,那最好还是退回到使用锁保护的临界区方案,代码逻辑会更清晰可控。
AtomicInteger 的 compareAndSet、get 等方法都自带 volatile 变量的内存语义。也就是说,状态的变更能够保证对所有线程立即可见。这是一个巨大的优势。
但是,有个细节需要警惕:如果你使用 lazySet 方法来设置最终状态(比如标记为 TERMINATED),要知道它并不提供完整的“释放屏障”,其他线程可能会在极短时间内读到旧值。
所以,总结一下:
status.get() 的状态判断逻辑(例如 if (status.get() == RUNNING))是可靠的,因为 get() 是 volatile 读。lazySet,除非你明确了解并能接受那几纳秒到微秒级的可见性延迟。compareAndSet 成功之后,立刻去依赖其他非 volatile 字段的值,因为它们的内存可见性不一定已经同步更新了。说到底,技术选型要看场景。AtomicInteger 做状态机,胜在轻量、无锁、性能高,尤其适合那些状态转换快、竞争不激烈的内部组件。把它用对了地方,代码就能既简洁又健壮。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8