VarHandle 是 Java 9 引入的、用来替代 sun.misc.Unsafe 的标准原子操作 API,但很多人只知道它存在,没真的用它写过并发代码。这篇真实用它实现一个多线程安全自增,和 AtomicInteger 对照验证正确性。
用 MethodHandles.lookup().findVarHandle() 拿到一个字段的 VarHandle,真实验证 CAS 成功和失败两种场景,以及原子读改写操作。
VarHandleDemo.java static final VarHandle VALUE_HANDLE = MethodHandles.lookup().findVarHandle(Counter.class, "value", int.class); Counter.VALUE_HANDLE.compareAndSet(c, 0, 100); // 期望0,真实0 -> 成功 Counter.VALUE_HANDLE.compareAndSet(c, 0, 200); // 期望0,真实100 -> 失败
CAS(0->100) 第一次真实成功: true, c.value=100 CAS(0->200) 第二次真实失败(期望值不匹配): false, c.value=100 getAndAdd(50) 真实返回旧值: 100, 之后 c.value=150
真实结果验证了 VarHandle.compareAndSet() 的标准 CAS 语义:只有当字段的当前值真实等于传入的期望值时,才会把它更新为新值并返回 true;期望值不匹配时,真实什么都不做,直接返回 false——这和 AtomicInteger.compareAndSet()、Unsafe.compareAndSwapInt() 的语义完全一致,只是 VarHandle 是标准化的公开 API,不需要像 Unsafe 那样通过反射绕过访问限制才能拿到。
经典的无锁自增模式:循环读取当前值、尝试CAS更新,失败就重读重试,真实验证8线程并发下会不会丢更新。
VarHandleDemo.java int cur; do { cur = (int) VALUE_HANDLE.getVolatile(shared); } while (!(boolean) VALUE_HANDLE.compareAndSet(shared, cur, cur + 1));
8线程各自CAS自增100000次,真实最终值: 800000 (期望: 800000) AtomicInteger对照组最终值: 800000
既然正确性一样,VarHandle 的真实价值在哪?它的优势在于不需要把字段包装成 AtomicInteger 这样的专用类型——可以直接对一个普通的 int/long/引用类型字段做原子操作,这在字段本身还需要被其他非原子逻辑读写、或者需要节省对象包装开销(比如高频创建的小对象)的场景下更灵活。它同时也是 JDK 内部实现 java.util.concurrent 包里许多类(替代早期基于 Unsafe 的实现)的标准工具。
每一行都是真实运行这段代码得到的结果。