VarHandle and Memory Ordering
VarHandle, in java.lang.invoke (Java 9), is a typed reference to a variable, such as a field or an array element, that lets you read and write it with chosen memory-ordering guarantees and perform atomic operations like compare-and-set on it. It is the modern, supported replacement for the internal sun.misc.Unsafe class, and the JDK's own concurrent classes are built on it. Most application code never needs it; it is for library authors and for understanding how java.util.concurrent works. In this lesson, you will learn how to create a VarHandle, use its atomic methods, and what the access modes mean.
Creating a VarHandle
static final VarHandle COUNT;
static {
try {
COUNT = MethodHandles.lookup()
.findVarHandle(Counter.class, "count", int.class);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}MethodHandles.arrayElementVarHandle(int[].class) creates one for array elements.
Access Modes
From weakest (fastest) to strongest:
| Mode | Methods | Guarantee |
|---|---|---|
| Plain | get, set | Like a normal field: no ordering or visibility promises |
| Opaque | getOpaque, setOpaque | Each access really happens and is eventually visible; no ordering with other variables |
| Acquire / Release | getAcquire, setRelease | A release write is seen, with everything written before it, by a matching acquire read |
| Volatile | getVolatile, setVolatile | Same as a volatile field: a single total order |
| Atomic updates | compareAndSet, getAndAdd, getAndSet | Atomic, with volatile ordering |
Release/acquire is the key idea: a setRelease acts like a fence that "publishes" every earlier write, and a getAcquire that sees that value is guaranteed to see those earlier writes too. It's the same happens-before pattern as a volatile flag (see "Java Memory Model and Happens-Before"), but slightly cheaper on some CPUs.
Example: Atomic Updates and Release/Acquire Publishing
Let's see an example that counts with a VarHandle and publishes data with release/acquire.
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Handles {
int count;
String message;
boolean ready;
static final VarHandle COUNT;
static final VarHandle READY;
static {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
COUNT = lookup.findVarHandle(Handles.class, "count", int.class);
READY = lookup.findVarHandle(Handles.class, "ready", boolean.class);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
public static void main(String[] args) throws InterruptedException {
Handles h = new Handles();
try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
for (int t = 0; t < 4; t++) {
pool.submit(() -> {
for (int i = 0; i < 50_000; i++) {
COUNT.getAndAdd(h, 1);
}
});
}
}
System.out.println("count = " + (int) COUNT.getVolatile(h));
Thread reader = new Thread(() -> {
while (!(boolean) READY.getAcquire(h)) {
Thread.onSpinWait();
}
System.out.println("Reader sees: " + h.message);
});
reader.start();
h.message = "Hello from Advitha";
READY.setRelease(h, true);
reader.join();
}
}count = 200000
Reader sees: Hello from AdvithaExplanation: COUNT.getAndAdd(h, 1) atomically adds one to the plain int field, so four threads reach exactly 200,000 without AtomicInteger. Then the main thread writes message normally and publishes it with setRelease on ready. The reader spins with getAcquire until it sees true; acquire/release guarantees it then sees the message written before the release.
When to Use VarHandle
Common Mistakes
Mixing plain reads with release writes and expecting visibility. Pair setRelease with getAcquire (or stronger).
Getting the field type wrong in findVarHandle. It throws when the class initializes.
Forgetting the cast on the result. get methods return Object-typed values that must be cast, such as (int).
Practice
- Easy: Create a
VarHandlefor alongfield and usegetAndAddto sum numbers from two threads. - Harder: Use
MethodHandles.arrayElementVarHandle(int[].class)to increment elements of a shared array atomically from four threads.
Key Takeaways
Was this tutorial helpful?