skip to content

What are the AtomicFieldUpdater classes, and why would you use one instead of wrapping every field in an AtomicInteger or AtomicReference?

level: seniorimportance: should knowfreq 30%

answer

  1. One shared updater per class, not one Atomic object per instance
  2. Saves memory: no wrapper object/pointer per instance
  3. Field must be volatile + non-static + accessible
  4. Reflection at newUpdater() time; slower per call
  5. Superseded by VarHandle (Java 9)

basics

~20 s

AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, and AtomicReferenceFieldUpdater let you do atomic updates (like compareAndSet) on a plain volatile field of a class. You use one shared updater per class instead of giving every object its own Atomic wrapper object, which saves memory when you have huge numbers of instances.

solid answer

~50 s

The atomic field updaters (AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater) provide reflection-based atomic operations such as compareAndSet, getAndAdd, and getAndSet on a designated volatile field of a class, without that field being wrapped in an Atomic object. The motivation is memory: an AtomicInteger field is an extra heap object and pointer per instance, so for millions of nodes (linked-list/queue nodes, framework internals) the per-object overhead is significant. The updater is a single static final instance shared across all objects of the class, so each instance carries only a primitive/volatile field. Constraints: the field must be volatile, non-static, and accessible (typically package-private or same-class), the updater is created with reflection at class init, and it is slower per-call than a direct Atomic due to reflective access checks. Today VarHandle is the modern, faster, more general replacement, but updaters still appear in older JDK and library code.

go deeper

for a junior

Knows that AtomicInteger/AtomicReference give thread-safe updates; may not have heard of field updaters. That is fine at this level.

for a middle

Can explain that field updaters perform atomic CAS on a volatile field without a per-instance wrapper, and recall the volatile requirement.

for a senior

Articulates the memory-vs-speed trade-off, the volatile/non-static/accessible constraints, reflective creation, and that VarHandle is the modern replacement.

for a principal

Reasons about when the optimization pays off (instance counts, cache locality), the historical use in lock-free JDK/library internals, and would steer new code to VarHandle while explaining the migration cost.

## The problem being solved In Java, ordinary fields are not safe to update from multiple threads without coordination. The `java.util.concurrent.atomic` package gives you lock-free, thread-safe updates built on the hardware **compare-and-swap (CAS)** instruction. The most familiar tools are wrapper objects: `AtomicInteger`, `AtomicLong`, `AtomicReference`. You hold one of these objects and call `incrementAndGet()`, `compareAndSet(expected, new)`, etc. **CAS** means: "atomically, if this memory location currently holds value X, set it to Y and report success; otherwise do nothing and report failure." It is the primitive that lets many threads update a value without a lock. ### Why wrappers can be wasteful An `AtomicInteger` is a full Java object on the heap. If a class needs one atomically-updatable counter, giving each instance its own `AtomicInteger` means: (1) an extra object header (~12-16 bytes) per instance, (2) an extra reference field pointing to it, and (3) an extra allocation and an extra pointer-dereference on every access. For a class you instantiate millions of times — think nodes in a non-blocking queue, or framework bookkeeping objects — that overhead is real, both in heap footprint and in cache behavior (the counter lives in a separate object, hurting locality). ## What AtomicFieldUpdater is The **atomic field updater** classes invert the design. Instead of wrapping the field in an object, you keep the field as a plain `volatile int` / `volatile long` / `volatile <ReferenceType>` directly inside your class, and you create **one shared updater object per class** that knows how to perform atomic operations on that named field of any instance. The three classes are: - `AtomicIntegerFieldUpdater<T>` - `AtomicLongFieldUpdater<T>` - `AtomicReferenceFieldUpdater<T,V>` (V is the field's reference type) You obtain one with a static factory: `AtomicIntegerFieldUpdater.newUpdater(MyClass.class, "count")`. It uses **reflection** at creation time to find the field and verify constraints, then offers methods like `get(obj)`, `set(obj, val)`, `compareAndSet(obj, expect, update)`, `getAndIncrement(obj)`, `getAndAdd(obj, delta)`. Every method takes the target object as the first argument — the updater itself is stateless with respect to any particular instance. ### The constraints (and why) 1. **The field must be declared `volatile`.** The updater provides atomicity and ordering on top of the volatile field's visibility guarantees; the contract requires it. 2. **The field must be non-static** (these update instance fields). 3. **The field must be accessible from the code creating the updater** — generally it must be visible per Java access rules (commonly the field is in the same class or package). Updaters cannot reach a field they couldn't legally see; otherwise creation throws. 4. For `AtomicLongFieldUpdater`, the field is `long`; for `AtomicIntegerFieldUpdater`, `int`; for the reference one, the actual field type must match the V type argument. Violations are detected when you call `newUpdater(...)`, which throws (e.g. `IllegalArgumentException` / `RuntimeException` wrapping the reflective failure) — so you typically create the updater in a `static final` field and any mistake fails fast at class initialization. ## The memory win, concretely ``` With AtomicInteger field: [MyNode header][ref ->][AtomicInteger header][int value] With updater: [MyNode header][volatile int value] ``` The updater version removes one object and one reference per instance. The trade-off is **per-call cost**: the updater historically does an access/type check and goes through a less-direct path, so a single operation is somewhat slower than calling a method on an `AtomicInteger` directly. The design bet is that you pay slightly more per operation to save a lot of memory across many instances — worthwhile only when the instance count is large. ## Where you actually see it Updaters appear inside the JDK itself and in libraries written before VarHandle existed (Java 9). For example, non-blocking data structures and reactive-streams implementations historically used `AtomicReferenceFieldUpdater` to CAS a `next`/`state` field on huge numbers of small node/subscription objects. In application code they are relatively rare and considered a micro-optimization. ## The modern successor Since Java 9, **`VarHandle`** (from `java.lang.invoke`) is the recommended replacement. A `VarHandle` to a field offers the same field-targeted atomic operations plus a full range of memory-ordering modes, is type-checked, and the JIT optimizes it to direct machine operations — making it faster and far more general than the reflective updaters. New code should prefer `VarHandle`; updaters remain mainly for understanding and maintaining existing code. ## Summary Atomic field updaters trade a tiny per-operation cost for a large per-instance memory saving by performing CAS-style atomic ops directly on a class's `volatile` field via a single shared, reflection-built updater object — historically used in high-instance-count internals, now superseded by `VarHandle`.

  • Why must the field be declared volatile when used with an atomic field updater?
    The updater supplies atomicity/ordering on top of the field's visibility guarantee, and its contract relies on volatile semantics for correct cross-thread visibility of the value; newUpdater rejects a non-volatile field at creation time.
  • When is the per-instance memory saving actually worth the slower per-call cost?
    When the class is instantiated in very large numbers (millions of small objects like queue nodes), so eliminating one wrapper object + reference per instance dominates the modest extra cost paid on each atomic operation.

saying these in an interview costs you the question

  • Saying the updater can target a non-volatile field — it requires volatile
  • Claiming updaters are faster per-call than AtomicInteger — they trade speed for memory
  • Thinking each instance needs its own updater — it is one static shared updater
  • Believing updaters can update static fields — they update instance fields only
  • Recommending updaters over VarHandle in new code

context