atomicintegerfieldupdater 是用于对已有 volatile int 字段进行原子操作的工具,复用字段避免对象开销,要求字段为 volatile int、非 static、访问可见且命名精确匹配。

AtomicIntegerFieldUpdater 是 Java 里一个“给已有对象字段加原子性”的工具,不是让你新建一个原子变量,而是直接对类里已有的 volatile int 字段做线程安全更新——它不改类结构、不增内存开销,但要求你写得非常小心。
为什么不能直接用 AtomicInteger?
因为 AtomicInteger 是个新对象,每次都要实例化;而 AtomicIntegerFieldUpdater 复用你原有对象里的字段,适合高并发下大量对象共存的场景(比如每条订单、每个连接、每个任务状态)。省的是对象头 + 引用 + GC 压力,不是省几行代码。
- 你有十万
User实例,每个要记登录次数 → 用AtomicIntegerFieldUpdater可复用volatile int loginCount字段,避免额外十万AtomicInteger - 你只是临时计数或单次状态切换 → 直接用
AtomicInteger更简单,别硬套 - 字段不是
volatile、是static或private且调用方不在同包 →newUpdater运行时抛RuntimeException,不是编译错
字段声明和 updater 创建的硬性条件
这些不是建议,是运行时校验失败就炸:
- 字段必须是
volatile int,不能是Integer、final int、static int - 字段访问修饰符必须让 updater 创建者“看得见”:如果 updater 写在
com.foo.Task类里,字段至少得是protected、package-private(默认)或public;private字段即使在同一类也不行(Java 反射限制) -
newUpdater必须用静态 final 声明:private static final AtomicIntegerFieldUpdater<task> STATE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(Task.class, "state");</task> - 字段名字符串必须完全匹配,大小写、拼写差一个字母,运行时才报
IllegalArgumentException
compareAndSet 和 getAndIncrement 怎么选?
它们都基于 CAS,但语义不同:
-
compareAndSet(obj, expect, update):适合状态机控制,比如只允许从0(INIT)→1(RUNNING),中间跳变或重复设置会失败,返回false -
getAndIncrement(obj)、decrementAndGet(obj):适合计数器,无条件自增/减,返回新值;底层是循环 CAS,但封装好了,不用手动重试 - 别用
weakCompareAndSet:JDK 9+ 已标记为@Deprecated,语义弱、顺序不可靠,没正当理由别碰
示例片段:
class Task {
volatile int state = 0; // ✅ 必须 volatile
}
private static final AtomicIntegerFieldUpdater<Task> STATE_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(Task.class, "state");
// 状态跃迁:仅当当前是 0 才设为 1
boolean started = STATE_UPDATER.compareAndSet(task, 0, 1);
// 计数器:无条件 +1,返回更新后值
int newCount = STATE_UPDATER.incrementAndGet(task);
容易被忽略的坑:继承、包可见性与 Unsafe 底层限制
很多人栽在“我以为能用,结果运行时报错”:
- 子类想更新父类的
volatile int status?不行。updater 必须在父类里定义,或字段移到子类中声明 - updater 定义在
com.a.Service,字段在com.b.model.User且是protected?跨包不可见,除非字段是public或 updater 移到同包 - 它底层依赖
Unsafe.compareAndSwapInt,所以字段偏移量(offset)是在类加载时计算的;如果字段被 JVM 优化重排(极少见),或用了某些字节码增强框架(如 Lombok @Data + @Setter),可能破坏字段布局,导致 CAS 失效
真正要用它,得把字段、updater、使用逻辑三者绑死在一个包里,字段不继承、不代理、不靠 Lombok 自动生成——越“原始”,越稳。










