字段更新器
你有一个类,字段很多,但不想为每个字段都创建 AtomicInteger 对象——太浪费内存了。
怎么办?
AtomicIntegerFieldUpdater 就是来解决这个问题的:它允许你对普通字段进行原子更新,不需要包装类。
核心思想
字段更新器(Field Updater)是一种无对象包装的原子操作方案。
传统方式:每次 new 一个 AtomicInteger
┌─────────────────────────────────────────────┐
│ Student ──► age: AtomicInteger │
│ Student ──► score: AtomicInteger │
│ 如果有 10000 个 Student,就创建 20000 个对象 │
└─────────────────────────────────────────────┘
字段更新器:共享Updater,操作原对象字段
┌─────────────────────────────────────────────┐
│ Updater(AtomicIntegerFieldUpdater) ──► Student.age │
│ Updater(AtomicIntegerFieldUpdater) ──► Student.score │
└─────────────────────────────────────────────┘三种 Updater
| Updater | 字段类型 | 说明 |
|---|---|---|
| AtomicIntegerFieldUpdater | int | 原子更新 int 字段 |
| AtomicLongFieldUpdater | long | 原子更新 long 字段 |
| AtomicReferenceFieldUpdater | 引用类型 | 原子更新引用字段 |
约束条件
使用字段更新器有严格要求:
- 字段必须是 volatile 的
- 字段类型必须匹配(int/long/Reference)
- 字段可见性:只能是 public 或包内可见,private 字段需要特殊处理
- 不能更新 final 字段
java
public class UpdaterRestrictions {
// ❌ 不能是 private(除非用反射,且需要 setAccessible(true))
// private volatile int value1;
// ✅ 可以是 package-private
volatile int value2;
// ✅ 可以是 protected
protected volatile int value3;
// ✅ 可以是 public
public volatile int value4;
}代码演示
AtomicIntegerFieldUpdater
java
public class AtomicIntegerFieldUpdaterDemo {
// 必须使用 volatile 修饰
private volatile int count = 0;
private static final AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterDemo> updater =
AtomicIntegerFieldUpdater.newUpdater(
AtomicIntegerFieldUpdaterDemo.class,
"count"
);
public void increment() {
updater.incrementAndGet(this);
}
public int get() {
return updater.get(this);
}
public static void main(String[] args) throws InterruptedException {
AtomicIntegerFieldUpdaterDemo demo = new AtomicIntegerFieldUpdaterDemo();
int threadCount = 100;
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
demo.increment();
}
}).start();
}
Thread.sleep(2000);
System.out.println("计数结果: " + demo.get()); // 应该是 100000
}
}AtomicLongFieldUpdater
java
public class AtomicLongFieldUpdaterDemo {
private volatile long value = 0L;
private static final AtomicLongFieldUpdater<AtomicLongFieldUpdaterDemo> updater =
AtomicLongFieldUpdater.newUpdater(
AtomicLongFieldUpdaterDemo.class,
"value"
);
public void add(long delta) {
updater.addAndGet(this, delta);
}
public long get() {
return updater.get(this);
}
public static void main(String[] args) {
AtomicLongFieldUpdaterDemo demo = new AtomicLongFieldUpdaterDemo();
for (int i = 1; i <= 100; i++) {
demo.add(i);
}
System.out.println("累加结果: " + demo.get()); // 5050
}
}AtomicReferenceFieldUpdater
java
public class AtomicReferenceFieldUpdaterDemo {
private volatile String name = "初始值";
private static final AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterDemo, String> updater =
AtomicReferenceFieldUpdater.newUpdater(
AtomicReferenceFieldUpdaterDemo.class,
String.class,
"name"
);
public void update(String newName) {
updater.set(this, newName);
}
public boolean compareAndSet(String expected, String newValue) {
return updater.compareAndSet(this, expected, newValue);
}
public String get() {
return updater.get(this);
}
public static void main(String[] args) {
AtomicReferenceFieldUpdaterDemo demo = new AtomicReferenceFieldUpdaterDemo();
System.out.println("初始值: " + demo.get());
// CAS 更新
boolean success = demo.compareAndSet("初始值", "新值");
System.out.println("CAS 更新结果: " + success); // true
System.out.println("当前值: " + demo.get()); // 新值
// 再次 CAS(会失败)
success = demo.compareAndSet("初始值", "另一个值");
System.out.println("再次 CAS 结果: " + success); // false
System.out.println("当前值: " + demo.get()); // 仍然是"新值"
}
}实际应用:更新已有对象的字段
java
public class UpdateObjectField {
private static class User {
volatile String name;
volatile int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
private final User user = new User("张三", 25);
private static final AtomicReferenceFieldUpdater<User, String> nameUpdater =
AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name");
private static final AtomicIntegerFieldUpdater<User> ageUpdater =
AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
public static void main(String[] args) {
User user = new User("张三", 25);
System.out.println("初始: " + user);
// 更新 name
nameUpdater.set(user, "李四");
System.out.println("更新 name 后: " + user);
// CAS 更新 age
ageUpdater.compareAndSet(user, 25, 30);
System.out.println("更新 age 后: " + user);
// 递增 age
ageUpdater.incrementAndGet(user);
System.out.println("递增后: " + user);
}
}适用场景
字段更新器适合这些情况:
- 不想为每个实例创建包装对象——节省内存
- 需要对已存在对象的字段进行原子更新——无需修改类设计
- 大量同类对象的某个字段需要频繁更新——如游戏中的角色坐标、库存数量等
注意事项
- volatile 是必须的:没有 volatile 修饰的字段无法保证可见性
- Updater 不是线程安全容器:它只负责原子操作,字段本身的可见性依赖 volatile
- 构造函数中的值:Updater 无法保证构造函数中的原子性
- 性能:通常比 Atomic* 包装类稍慢,但省内存
什么时候不用
- 实例数量不多:直接用
AtomicInteger等包装类更简单 - 字段是 private 且无法修改可见性:Updater 无效
- 需要更复杂的复合操作:直接用 synchronized 更安全
总结
字段更新器的核心价值是在不创建额外对象的情况下,实现字段的原子更新。
场景判断:
- 实例少、字段简单 ──► 直接用 AtomicInteger
- 实例多、字段频繁更新 ──► 考虑字段更新器
- 需要复合操作 ──► 用 synchronized