Advanced Atomics
Beyond AtomicInteger and AtomicLong, java.util.concurrent.atomic provides tools for complex state management in a lock-free way.
AtomicReference #
AtomicReference provides atomic updates for object references. It is the core of most lock-free data structures that need to link or unlink nodes.
Canonical Usage: Use AtomicReference when you have a complex state object that must be updated as a single unit or when you need to perform an atomic “swap” of an entire configuration object.
AtomicReference<Config> config = new AtomicReference<>(new Config());
// Atomic swap
Config oldConfig = config.getAndSet(newConfig);
// Complex CAS update
config.updateAndGet(c -> c.withNewProperty("new-value"));
Implementation Details #
AtomicReference Structure #
public class AtomicReference<V> implements java.io.Serializable {
private static final VarHandle VALUE = MhUtil.findVarHandle(
MethodHandles.lookup(), "value", Object.class);
@SuppressWarnings("serial") // Conditionally serializable
private volatile V value;
}
Core Operations #
compareAndSet:
public final boolean compareAndSet(V expectedValue, V newValue) {
return VALUE.compareAndSet(this, expectedValue, newValue);
}
getAndSet:
public final V getAndSet(V newValue) {
return (V)VALUE.getAndSet(this, newValue);
}
Functional Updates (Java 8+) #
public final V getAndUpdate(UnaryOperator<V> updateFunction) {
V prev = get(), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.apply(prev);
if (weakCompareAndSetVolatile(prev, next))
return prev;
haveNext = (prev == (prev = get()));
}
}
public final V updateAndGet(UnaryOperator<V> updateFunction) {
V prev = get(), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = updateFunction.apply(prev);
if (weakCompareAndSetVolatile(prev, next))
return next;
haveNext = (prev == (prev = get()));
}
}
Accumulate Operations #
public final V getAndAccumulate(V x,
BinaryOperator<V> accumulatorFunction) {
V prev = get(), next = 0;
for (boolean haveNext = false;;) {
if (!haveNext)
next = accumulatorFunction.apply(prev, x);
if (weakCompareAndSetVolatile(prev, next))
return prev;
haveNext = (prev == (prev = get()));
}
}
Memory Ordering Variants #
Volatile (default):
public final boolean compareAndSet(V expect, V update) {
return VALUE.compareAndSet(this, expect, update);
}
Plain (weaker semantics):
public final boolean weakCompareAndSetPlain(V expect, V update) {
return VALUE.weakCompareAndSetPlain(this, expect, update);
}
Lazy Set:
public final void lazySet(V newValue) {
VALUE.setRelease(this, newValue);
}
FieldUpdaters #
AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, and AtomicReferenceFieldUpdater allow you to perform atomic updates on volatile fields of other classes using reflection.
Canonical Usage: Use field updaters instead of wrapping a field in an AtomicInteger when you have thousands or millions of small objects (e.g., millions of nodes in a massive graph). An AtomicInteger is a separate object with its own header (12-16 bytes). A field updater is a single static instance that can update fields in any number of target objects without extra object overhead.
public class Node {
private volatile int state;
private static final AtomicIntegerFieldUpdater<Node> stateUpdater =
AtomicIntegerFieldUpdater.newUpdater(Node.class, "state");
public void lock() {
stateUpdater.compareAndSet(this, 0, 1);
}
}
Implementation Details #
FieldUpdater Creation #
@CallerSensitive
public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
String fieldName) {
return new AtomicIntegerFieldUpdaterImpl<U>
(tclass, fieldName, Reflection.getCallerClass());
}
The factory method:
- Validates the field exists and is
volatile int - Checks accessibility using reflection
- Returns a concrete
AtomicIntegerFieldUpdaterImplinstance
Core Operations #
compareAndSet:
public abstract boolean compareAndSet(T obj, int expect, int update);
getAndSet:
public abstract int getAndSet(T obj, int newValue);
getAndIncrement:
public int getAndIncrement(T obj) {
return getAndAdd(obj, 1);
}
Implementation Class #
The concrete implementation uses Unsafe:
private static final class AtomicIntegerFieldUpdaterImpl<T>
extends AtomicIntegerFieldUpdater<T> {
private final long offset;
private final Unsafe unsafe;
AtomicIntegerFieldUpdaterImpl(Class<T> tclass, String fieldName, Class<?> caller) {
// Reflection validation and offset calculation
this.offset = U.objectFieldOffset(field);
this.unsafe = U;
}
public boolean compareAndSet(T obj, int expect, int update) {
return unsafe.compareAndSetInt(obj, offset, expect, update);
}
public int getAndSet(T obj, int newValue) {
return unsafe.getAndSetInt(obj, offset, newValue);
}
}
Memory Layout Efficiency #
Without FieldUpdater (AtomicInteger wrapper):
class Node {
AtomicInteger state = new AtomicInteger(); // 12-16 byte header + 4 byte int
// Total: ~16-20 bytes per Node
}
With FieldUpdater:
class Node {
volatile int state; // 4 bytes
// Total: 4 bytes per Node
// Shared updater instance: one per class
}
For 1M nodes: 12-16MB savings
Limitations #
- Field must be volatile: Non-volatile fields cause
IllegalArgumentException - No final fields: Final fields cannot be updated atomically
- Access control: Field must be accessible to the caller
- Weaker guarantees: Only guarantees atomicity with respect to other invocations on the same updater
- Performance overhead: Reflection-based validation on creation
Best Practices #
// Correct: static final field updater
private static final AtomicIntegerFieldUpdater<Node> stateUpdater =
AtomicIntegerFieldUpdater.newUpdater(Node.class, "state");
// Anti-pattern: creating updaters repeatedly
public void update() {
// DON'T DO THIS - creates new updater on every call
AtomicIntegerFieldUpdater<Node> updater =
AtomicIntegerFieldUpdater.newUpdater(Node.class, "state");
updater.incrementAndGet(this);
}
AtomicStampedReference #
AtomicStampedReference maintains an object reference along with an integer “stamp” that can be updated atomically. It is designed to solve the ABA problem in lock-free programming, where a value is changed from A to B and then back to A, causing a simple CAS to succeed incorrectly.