VarHandle 仅支持 int 和 long 类型的原子位操作方法(如 getAndBitwiseOr),要求字段为 private final 原始类型,不支持包装类;其行为是带 acquire-release 语义的“读-改-写”,返回旧值,不可定制内存序。

VarHandle 的位操作方法名和类型限制
Java 的 VarHandle 并不直接提供类似 getAndBitwiseOr 这样的“位运算+获取”组合方法对 long 字段——它只在整数类型(int、long)上支持原子位操作,且仅限于「修改并返回旧值」这一类:比如 getAndBitwiseOr、getAndBitwiseAnd、getAndBitwiseXor。这些方法要求目标字段必须是 int 或 long,其他类型(如 short、byte、包装类)会抛出 UnsupportedOperationException。
常见错误现象:IllegalArgumentException: Unsupported access mode for type class java.lang.Integer 或运行时 UnsupportedOperationException,往往是因为传入了非原始整型字段,或试图对 volatile long 字段调用 getAndAdd 以外的原子方法却未确认类型匹配。
- 仅
int和long字段支持位操作原子方法 - 字段声明必须是
private final(静态或实例),且不能是final修饰的常量编译期值(否则lookup.findVarHandle会失败) - 不支持
Long包装类字段;必须是原始long
如何正确创建 long 字段的 VarHandle 实例
创建句柄时,必须使用 MethodHandles.privateLookupIn(推荐)或 MethodHandles.lookup(),但后者对私有字段需要先调用 ensureInitialized 或确保在同一个模块内。若字段是实例字段,findVarHandle 第三个参数必须显式写为 long.class,不可省略或误写为 Long.class。
示例中容易踩的坑:
- 用
MethodHandles.lookup().findVarHandle(..., long.class)访问其他类的私有字段 → 抛IllegalAccessException - 静态字段误用
findVarHandle而非findStaticVarHandle→ 报NoSuchFieldException - 字段类型写成
Long.TYPE(等价于long.class)虽可工作,但语义模糊,建议直写long.class
正确写法(实例字段):
private static final VarHandle FLAGS_HANDLE;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(MyClass.class, MethodHandles.lookup());
FLAGS_HANDLE = lookup.findVarHandle(MyClass.class, "flags", long.class);
} catch (Throwable t) {
throw new ExceptionInInitializerError(t);
}
}
getAndBitwiseOr 等方法的实际行为与内存语义
getAndBitwiseOr 是「读-修改-写」的原子操作:它先读取当前值,再与参数按位或,把结果写回,并返回**修改前的原始值**。整个过程具有 memory_order_acq_rel 语义(Java 中对应 getAndUpdate 的默认屏障强度),即兼具 acquire 读和 release 写的内存屏障效果。
这意味着:该操作能防止其前后的普通读写被重排序,且对其他线程可见 —— 但不等价于 volatile 读写(后者是 acquire/release,而这是更强的 acq_rel)。如果你只需要设置某一位且不关心旧值,应改用 set + getVolatile 配合循环 CAS,而非无谓地获取旧值。
- 返回值是操作前的值,不是新值;若需新值,得自己再
get一次(但可能已被其他线程改) - 不支持自定义内存序(如 relaxed 版本);所有位操作方法都固定使用最强屏障
- 若并发冲突频繁,性能未必优于
compareAndSet循环,因每次调用都触发完整屏障
替代方案:用 compareAndSet 实现更灵活的位操作
当需要 relaxed 内存序、或需基于当前值做条件判断(如“仅当某位未置位时才设置”),compareAndSet + 显式位运算仍是更可控的选择。它避免了 getAndBitwiseOr 的强制屏障开销,也绕过类型限制(理论上可用于任何可读写的字段)。
示例:原子设置第 3 位(bit 3),但仅当该位当前为 0:
long current;
long next;
do {
current = (long) FLAGS_HANDLE.getVolatile(this);
if ((current & 0x8L) != 0) break; // 已置位,退出
next = current | 0x8L;
} while (!FLAGS_HANDLE.compareAndSet(this, current, next));
注意点:
-
getVolatile和compareAndSet需配对使用,否则可能读到陈旧值 - 循环体必须轻量;若竞争激烈,考虑退避(如
Thread.onSpinWait()) - 这种模式可移植到
int、long、甚至数组元素,而原生位方法仅限字段
真正容易被忽略的是:VarHandle 的位操作不是“魔法加速”,它底层仍依赖 CPU 的 CAS 指令(如 x86 的 lock or)。是否更快,取决于你的场景——简单单次置位用 getAndBitwiseOr 更简洁;复杂条件逻辑或高竞争下,手动 CAS 循环反而更稳、更透明。










