Skip to content

字段更新器

你有一个类,字段很多,但不想为每个字段都创建 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字段类型说明
AtomicIntegerFieldUpdaterint原子更新 int 字段
AtomicLongFieldUpdaterlong原子更新 long 字段
AtomicReferenceFieldUpdater引用类型原子更新引用字段

约束条件

使用字段更新器有严格要求:

  1. 字段必须是 volatile 的
  2. 字段类型必须匹配(int/long/Reference)
  3. 字段可见性:只能是 public 或包内可见,private 字段需要特殊处理
  4. 不能更新 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 &lt; threadCount; i++) {
            new Thread(() -> {
                for (int j = 0; j &lt; 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 &lt;= 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);
    }
}

适用场景

字段更新器适合这些情况:

  1. 不想为每个实例创建包装对象——节省内存
  2. 需要对已存在对象的字段进行原子更新——无需修改类设计
  3. 大量同类对象的某个字段需要频繁更新——如游戏中的角色坐标、库存数量等

注意事项

  1. volatile 是必须的:没有 volatile 修饰的字段无法保证可见性
  2. Updater 不是线程安全容器:它只负责原子操作,字段本身的可见性依赖 volatile
  3. 构造函数中的值:Updater 无法保证构造函数中的原子性
  4. 性能:通常比 Atomic* 包装类稍慢,但省内存

什么时候不用

  • 实例数量不多:直接用 AtomicInteger 等包装类更简单
  • 字段是 private 且无法修改可见性:Updater 无效
  • 需要更复杂的复合操作:直接用 synchronized 更安全

总结

字段更新器的核心价值是在不创建额外对象的情况下,实现字段的原子更新

场景判断:
- 实例少、字段简单 ──► 直接用 AtomicInteger
- 实例多、字段频繁更新 ──► 考虑字段更新器
- 需要复合操作 ──► 用 synchronized

基于 VitePress 构建