java.util.concurrent.atomic.AtomicIntegerFieldUpdater
是 Java 并发包中一个非常强大的工具,它允许你以原子方式更新指定对象的 volatile int
字段,而无需使用锁。这在实现高性能、非阻塞的并发算法时非常有用,就像 Netty 在其 ByteBuf
引用计数管理中所做的那样。
AtomicIntegerFieldUpdater
的核心作用AtomicIntegerFieldUpdater
的主要目的是实现字段级别的 CAS (Compare-And-Swap) 操作。
volatile int
字段。volatile
关键字是强制性的,因为它保证了字段在多线程间的可见性,并且阻止了编译器和处理器进行可能导致并发问题的重排序。synchronized
关键字或 Lock
接口,但它通过底层的硬件 CAS 指令来保证操作的原子性。这意味着读取、比较和更新这三个步骤是一个不可中断的单一操作。AtomicIntegerFieldUpdater
?传统的 AtomicInteger
只能操作一个独立的 int
变量。但如果你有一个类,它内部的一个 int
字段需要被原子更新,而你又不想为整个类或方法加锁,AtomicIntegerFieldUpdater
就派上用场了。
例如,在 Netty 的 AbstractReferenceCountedByteBuf
中,refCnt
(引用计数)是一个 ByteBuf
实例的内部字段。Netty 希望能在不阻塞 ByteBuf
实例的情况下,原子地修改它的 refCnt
,以最大化性能。AtomicIntegerFieldUpdater
正好满足了这个需求。
AtomicIntegerFieldUpdater
的基本用法使用 AtomicIntegerFieldUpdater
通常包括以下几个步骤:
volatile int
这是最关键的前提。你要操作的字段必须声明为 public volatile int
或 protected volatile int
或 private volatile int
(如果 Updater
也在同一个类中),并且不能是 static
或 final
。
Java
public class MyClass {
// 必须是 volatile int 类型
private volatile int myIntField;
// ... 其他代码 ...
}
AtomicIntegerFieldUpdater
实例Updater
是通过静态工厂方法 newUpdater()
来创建的。
Java
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class MyClass {
private volatile int myIntField;
// 1. 创建 AtomicIntegerFieldUpdater 实例
// 参数1: 要操作的对象的 Class 类型
// 参数2: 要操作的字段的名称 (字符串)
private static final AtomicIntegerFieldUpdater UPDATER =
AtomicIntegerFieldUpdater.newUpdater(MyClass.class, "myIntField");
public MyClass(int initialValue) {
this.myIntField = initialValue;
}
// ... 其他方法 ...
}
注意事项:
newUpdater
方法是线程安全的,通常只在类加载时执行一次,所以把它定义为 static final
字段是最佳实践。NoSuchFieldException
或 IllegalArgumentException
。Updater
进行原子操作创建 Updater
实例后,就可以使用它提供的方法来执行原子操作了。这些方法需要传入目标对象实例作为第一个参数,因为 Updater
是通用的,需要知道具体操作哪个对象的字段。
Java
public class MyClass {
private volatile int myIntField;
private static final AtomicIntegerFieldUpdater UPDATER =
AtomicIntegerFieldUpdater.newUpdater(MyClass.class, "myIntField");
public MyClass(int initialValue) {
this.myIntField = initialValue;
}
// 获取字段当前值
public int getMyIntField() {
return UPDATER.get(this);
}
// 原子地设置字段值
public void setMyIntField(int newValue) {
UPDATER.set(this, newValue);
}
// 原子地增加或减少字段值
public int incrementAndGet() {
return UPDATER.incrementAndGet(this); // myIntField++ 并返回新值
}
public int decrementAndGet() {
return UPDATER.decrementAndGet(this); // myIntField-- 并返回新值
}
// 核心的 CAS 操作:比较并设置
// 如果 myIntField 当前值等于 expect,则将其设置为 update,并返回 true;否则返回 false。
public boolean compareAndSet(int expect, int update) {
return UPDATER.compareAndSet(this, expect, update);
}
// 原子地累加指定值并返回旧值
public int getAndAdd(int delta) {
return UPDATER.getAndAdd(this, delta);
}
// 原子地设置新值并返回旧值
public int getAndSet(int newValue) {
return UPDATER.getAndSet(this, newValue);
}
public static void main(String[] args) {
MyClass obj = new MyClass(0);
// 示例使用
System.out.println("Initial value: " + obj.getMyIntField()); // 0
obj.incrementAndGet();
System.out.println("After increment: " + obj.getMyIntField()); // 1
obj.setMyIntField(10);
System.out.println("After set: " + obj.getMyIntField()); // 10
boolean success = obj.compareAndSet(10, 15); // 期望是10,更新为15
System.out.println("CAS success: " + success + ", current value: " + obj.getMyIntField()); // true, 15
success = obj.compareAndSet(10, 20); // 期望是10,但当前是15,所以失败
System.println("CAS success: " + success + ", current value: " + obj.getMyIntField()); // false, 15
}
}
AtomicIntegerFieldUpdater
在 Netty 中的应用在 Netty 的 AbstractReferenceCountedByteBuf
中,AtomicIntegerFieldUpdater
被用于原子地管理 refCnt
(引用计数)字段。
Java
// netty/buffer/AbstractReferenceCountedByteBuf.java (简化版)
public abstract class AbstractReferenceCountedByteBuf extends AbstractByteBuf {
// refCnt 字段,volatile 保证可见性,private 封装实现
private volatile int refCnt;
// 创建 Updater 实例,操作 this 对象的 refCnt 字段
private static final AtomicIntegerFieldUpdater REFCNT_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(AbstractReferenceCountedByteBuf.class, "refCnt");
protected AbstractReferenceCountedByteBuf(int maxCapacity) {
super(maxCapacity);
// 初始化 refCnt 为 1
// 注意这里不是通过 UPDATER.set(this, 1),而是直接设置,
// 因为在构造函数中,对象还未发布给其他线程,不存在并发问题
refCnt = 1;
}
@Override
public final ByteBuf retain(int increment) {
// ... 参数校验 ...
for (;;) { // 自旋循环,直到 CAS 成功
int refCnt = this.refCnt; // 读取当前值
// ... 各种校验 (如是否已释放, 是否溢出) ...
if (REFCNT_UPDATER.compareAndSet(this, refCnt, refCnt + increment)) { // 尝试 CAS 更新
return this; // 更新成功,跳出循环
}
// 失败则继续循环重试
}
}
@Override
public final boolean release(int decrement) {
// ... 参数校验 ...
for (;;) { // 自旋循环,直到 CAS 成功
int refCnt = this.refCnt; // 读取当前值
// ... 各种校验 (如是否小于减量) ...
if (REFCNT_UPDATER.compareAndSet(this, refCnt, refCnt - decrement)) { // 尝试 CAS 更新
if (refCnt == decrement) { // 如果引用计数归零
deallocate(); // 调用抽象方法释放资源
return true;
}
return false; // 更新成功,但未归零
}
// 失败则继续循环重试
}
}
protected abstract void deallocate(); // 留给子类实现具体的内存释放逻辑
}
解析:
refCnt
为什么是 private volatile int
? private
封装了实现细节,不允许外部直接修改,只能通过 retain()
和 release()
。volatile
保证了所有线程都能看到最新的 refCnt
值。refCnt = 1;
并没有使用 Updater
。这是因为在对象构造期间,对象还没有“发布”给其他线程,不存在并发访问的风险,直接赋值是最快的。for (;;)
循环: 无论是 retain()
还是 release()
,它们都包裹在一个 for (;;)
循环中。这是典型的 自旋 (Spinning) 模式。如果 compareAndSet
操作失败(说明在读取 refCnt
到尝试更新之间,有其他线程修改了它),线程不会被阻塞,而是会立即重新读取最新值并再次尝试,直到成功为止。deallocate()
的触发: 在 release()
方法中,当 compareAndSet
成功并且新的 refCnt
值等于 0
时,就会调用抽象的 deallocate()
方法,由具体的 ByteBuf
子类来执行实际的内存释放(如归还到内存池或释放直接内存)。适用场景:
volatile int
/long
/boolean
字段,且不希望引入传统锁的开销。AtomicStampedReference
或 AtomicMarkableReference
。ReentrantLock
)可能表现更好,因为它能让失败的线程挂起而不是持续消耗 CPU。volatile
修饰的非 static
、非 final
的 int
、long
或 boolean
字段(对应 AtomicIntegerFieldUpdater
、AtomicLongFieldUpdater
、AtomicReferenceFieldUpdater
)。newUpdater
的创建内部涉及反射,但这通常只发生一次,所以可以忽略。