bigywater.tech
Java · 286

VarHandle 内存屏障:从访问模式到可见性

VarHandle 把 plain、opaque、acquire/release、volatile 四档读写与 CAS 都放进一个对象。不同变量到底支持哪几种?类型写错抛什么?字节码是什么样?这篇逐项量。

31
VarHandle.AccessMode 的种数
4 / 31
final 字段 VarHandle 支持的访问模式数(只能读)
0
消息传递实验里 4 种写法 × 3 轮的违例次数(没有复现重排序)

访问模式

实测 · 本机执行输出
    AccessMode 总数:31
      字段 i   (int)  31/31
      字段 l   (long)  31/31
      字段 b   (boolean)  28/31 不支持:[getAndAdd, getAndAddAcquire, getAndAddRelease]
      字段 s   (String)  19/31 不支持:[getAndAdd, getAndAddAcquire, getAndAddRelease, getAndBitwiseOr, getAndBitwiseOrRelease, getAndBitwiseOrAcquire, getAndBitwiseAnd, getAndBitwiseAndRelease, getAndBitwiseAndAcquire, getAndBitwiseXor, getAndBitwiseXorRelease, getAndB
      字段 vi  (int,volatile)  31/31
      字段 f   (int,final)  4/31 不支持:[set, setVolatile, setRelease, setOpaque, compareAndSet, compareAndExchange, compareAndExchangeAcquire, compareAndExchangeRelease, weakCompareAndSetPlain, weakCompareAndSet, weakCompareAndSetAcquire, weakCompareAndSetRelease]…共27
      字段 d   (double)  22/31 不支持:[getAndBitwiseOr, getAndBitwiseOrRelease, getAndBitwiseOrAcquire, getAndBitwiseAnd, getAndBitwiseAndRelease, getAndBitwiseAndAcquire, getAndBitwiseXor, getAndBitwiseXorRelease, getAndBitwiseXorAcquire]
      int[] 元素       31/31
      String[] 元素    19/31 不支持:[getAndAdd, getAndAddAcquire, getAndAddRelease, getAndBitwiseOr, getAndBitwiseOrRelease, getAndBitwiseOrAcquire, getAndBitwiseAnd, getAndBitwiseAndRelease, getAndBitwiseAndAcquire, getAndBitwiseXor, getAndBitwiseXorRelease, getAndBit
      byte[] 视图 int  31/31
      byte[] 视图 long 31/31
      ByteBuffer 视图 int 31/31
      坐标类型 / 值类型(字段 i):[class Main] / int
      accessModeType(COMPARE_AND_SET)= (Main,int,int)boolean
      accessModeType(GET_AND_ADD)    = (Main,int)int

类型规则与异常

实测 · 本机执行输出
    I.set(m, 5)                                          → 成功
    I.set(m, 5L)(long 传给 int)                            → WrongMethodTypeException: cannot convert MethodHandle(VarHandle,Main,int)void to (VarHandle,Main,long)void
    I.set(m, (short) 5)(short 可以宽化到 int)                 → 成功
    I.set(m, "x")                                        → WrongMethodTypeException: cannot convert MethodHandle(VarHandle,Main,int)void to (VarHandle,Main,String)void
    I.set(m)(少一个参数)                                      → WrongMethodTypeException: cannot convert MethodHandle(VarHandle,Main,int)void to (VarHandle,Main)void
    I.set(null, 1)(null 对象)                              → NullPointerException: null
    int x = (int) I.get(m)                               → 成功
    long x = (long) I.get(m)(int 窄到 long 宽化)             → 成功
    Object x = I.get(m)(没有强转,返回 Object)                  → 成功
    F.set(m, 2)(final 字段 set)                            → UnsupportedOperationException: set
    F.compareAndSet(m, 1, 2)                             → UnsupportedOperationException: compareAndSet
    F.get(m)                                             → 成功
    LL.compareAndSet(m, 0, 1)(int 字面量对 long 字段)          → 成功
    LL.compareAndSet(m, 0L, 1L)                          → 成功
    I.getAndAdd(m, 3) 的返回类型 int                          → 成功
    S.getAndAdd(m, "x")(引用类型没有数值原子操作)                    → UnsupportedOperationException: getAndAdd
    I.compareAndSet(m, 5, 6) 返回 boolean                  → 成功
    I.get(m) 与 I.getVolatile / getOpaque / getAcquire    → 成功
    S.set(m, new StringBuilder())(类型不兼容的引用)              → ClassCastException: Cannot cast java.lang.StringBuilder to java.lang.String
    S.set(m, (CharSequence) "x")(静态类型是 CharSequence)     → 成功
    S.set(m, (Object) "x")(静态类型是 Object)                 → 成功

字节码

实测 · 本机执行输出
    static final java.lang.invoke.VarHandle I;
    8: getstatic #10 // Field I:Ljava/lang/invoke/VarHandle;
    13: invokevirtual #14 // Method java/lang/invoke/VarHandle.setRelease:(LMain;I)V
    16: getstatic #10 // Field I:Ljava/lang/invoke/VarHandle;
    20: invokevirtual #20 // Method java/lang/invoke/VarHandle.getAcquire:(LMain;)I
    24: getstatic #10 // Field I:Ljava/lang/invoke/VarHandle;
    31: invokevirtual #24 // Method java/lang/invoke/VarHandle.compareAndSet:(LMain;II)Z
    10: invokevirtual #60 // Method java/lang/invoke/MethodHandles$Lookup.findVarHandle:(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/i
    13: putstatic #10 // Field I:Ljava/lang/invoke/VarHandle;
  运行: 5 true 6

消息传递实验

实测 · 本机执行输出
    第 1 轮:纯普通读写违例 0;setRelease/getAcquire 违例 0;storeStoreFence/loadLoadFence 违例 0;volatile 违例 0
    第 2 轮:纯普通读写违例 0;setRelease/getAcquire 违例 0;storeStoreFence/loadLoadFence 违例 0;volatile 违例 0
    第 3 轮:纯普通读写违例 0;setRelease/getAcquire 违例 0;storeStoreFence/loadLoadFence 违例 0;volatile 违例 0

分步演示

VarHandle 与屏障
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,Apple Silicon(arm64)
    • 没有验证:真正的重排序复现(此实验 0 违例,没有借助 jcstress)、x86 上的行为差异、各访问模式在 JIT 里生成的机器指令、VarHandle 相对 Unsafe / Atomic 类的吞吐对比、MethodHandles.privateLookupIn 跨模块访问
    京ICP备2026067502号-1京公网安备11010502063487号