skip to content

If you declare a volatile array (e.g. volatile int[] data), which accesses get volatile semantics — and how do you get per-element visibility?

level: seniorimportance: should knowfreq 35%

answer

  1. volatile covers the reference, not the slots
  2. data[i]=x is a plain store
  3. AtomicIntegerArray / AtomicReferenceArray
  4. VarHandle arrayElementVarHandle getVolatile/setVolatile
  5. or publish immutable array via volatile ref

basics

~10 s

Only the array reference is volatile — reassigning the whole array variable is visible. Writing an element (data[i] = x) is NOT volatile. For per-element visibility use AtomicIntegerArray or VarHandle.

solid answer

~50 s

The volatile modifier applies to the variable it's attached to, which for volatile int[] data is the array reference, not the array's slots. So replacing the array (data = newArray) gets full volatile visibility and ordering, but an element write like data[3] = 7 is an ordinary, non-volatile store with no cross-thread visibility guarantee — another thread may never see it or may see it reordered. This trips people who expect 'volatile array' to mean 'volatile elements'. To get volatile/atomic semantics per element, use AtomicIntegerArray (or AtomicLongArray, AtomicReferenceArray), whose get/set/compareAndSet operate with the proper memory effects on individual slots, or use a VarHandle obtained via MethodHandles.arrayElementVarHandle for getVolatile/setVolatile/compareAndSet on array elements. Alternatively, publish a fully-built array once via the volatile reference and then treat it as immutable, which safely publishes all the elements written before the volatile store.

code

java · 13 lines
java
// Pitfall: only the reference is volatile
volatile int[] data = new int[10];
data[3] = 7;   // PLAIN write - not guaranteed visible to other threads

// Fix A: per-element volatile/atomic semantics
AtomicIntegerArray atomic = new AtomicIntegerArray(10);
atomic.set(3, 7);          // visible to other threads
atomic.incrementAndGet(3); // atomic per-element update

// Fix B: VarHandle on array elements
VarHandle VH = MethodHandles.arrayElementVarHandle(int[].class);
int[] arr = new int[10];
VH.setVolatile(arr, 3, 7);

go deeper

for a junior

May not know this nuance; at minimum should recall that volatile applies to a variable, not necessarily everything it points to.

for a middle

Knows the reference vs elements distinction and that element writes aren't volatile, even if unsure of the exact remedy.

for a senior

Names the remedies (AtomicIntegerArray/AtomicReferenceArray, VarHandle arrayElementVarHandle) and the safe-publication-of-immutable-array alternative.

for a principal

Reasons about VarHandle access modes and chooses per-element atomics vs immutable republication based on the read/write pattern and contention.

## What 'volatile' attaches to `volatile` is a modifier on a **variable**. For `volatile int[] data`, the variable is the **reference** to the array object — a pointer. So volatile semantics cover *operations on that reference*: ```java volatile int[] data = new int[10]; data = new int[20]; // VOLATILE write of the reference - visible/ordered int[] local = data; // VOLATILE read of the reference ``` What it does **not** cover is reads/writes of the **elements**: ```java data[3] = 7; // PLAIN store to a slot - NOT volatile int v = data[3]; // PLAIN load - NOT volatile ``` The array object's slots are ordinary memory. There is no language syntax to make individual elements volatile, so `data[3] = 7` carries none of the visibility/ordering guarantees; another thread reading `data[3]` may see a stale value or observe writes out of order. ## Why this surprises people The mental model 'volatile array == volatile elements' is wrong but intuitive. The modifier sits on the variable declaration, and the variable *is* the reference. The compiler has no way to attach per-slot barriers from that single keyword. ## Getting per-element semantics ### 1. Atomic array classes `java.util.concurrent.atomic` provides `AtomicIntegerArray`, `AtomicLongArray`, and `AtomicReferenceArray`. Their `get(i)`/`set(i, v)` carry volatile-like memory effects per element, and `compareAndSet(i, expect, update)`/`getAndAdd(i, delta)` give atomic read-modify-write per slot: ```java AtomicIntegerArray data = new AtomicIntegerArray(10); data.set(3, 7); // volatile-style write of element 3 int v = data.get(3); // volatile-style read data.incrementAndGet(3); // atomic per-element RMW ``` ### 2. VarHandle on array elements A `VarHandle` (the modern, type-safe replacement for `sun.misc.Unsafe`) can target array elements: ```java VarHandle VH = MethodHandles.arrayElementVarHandle(int[].class); int[] arr = new int[10]; VH.setVolatile(arr, 3, 7); // volatile element write int v = (int) VH.getVolatile(arr, 3); VH.compareAndSet(arr, 3, 7, 8); // atomic CAS on the element ``` This gives fine-grained control (volatile, acquire/release, opaque, plain modes) without boxing. ### 3. Safe publication of an immutable array If the array is built once and then read-only, you don't need per-element atomics. Write all elements, then store the reference into the volatile field **last**. The volatile write of the reference happens-before any read of that reference, and by transitivity it publishes all the element writes that preceded it: ```java int[] tmp = new int[n]; for (int i = 0; i < n; i++) tmp[i] = compute(i); data = tmp; // volatile publish: readers see fully-populated array ``` After this, treat the array as immutable — don't mutate slots, or you reintroduce the visibility gap. ## Summary - `volatile int[]` -> the **reference** is volatile; **elements are not**. - Per-element volatile/atomic -> `AtomicIntegerArray` / `AtomicReferenceArray` or a `VarHandle`. - Or publish a built-once array via the volatile reference and keep it immutable.

  • If I build an array fully and then assign it to a volatile reference, are the element values safely visible?
    Yes. The volatile write of the reference happens-before any read of it, so all element writes done before the assignment are published to a thread that reads the new reference — provided you then treat the array as immutable and don't mutate slots afterward.
  • What's the difference between AtomicIntegerArray and a VarHandle on an int[]?
    AtomicIntegerArray is a dedicated object wrapping the array with a fixed atomic API and slight memory overhead. A VarHandle operates directly on a plain int[] and offers multiple access modes (plain, opaque, acquire/release, volatile) for finer control, with no wrapper object.

Putting a 'live-updated' label on the address of a filing cabinet (the reference) doesn't make each drawer inside it live-updated. To track individual drawers you need a per-drawer mechanism (atomic array / VarHandle).

saying these in an interview costs you the question

  • Assuming 'volatile array' makes element writes visible across threads.
  • Writing data[i] = x from one thread and expecting another to see it without atomics or republishing.
  • Thinking you can declare individual array elements volatile with syntax (you can't).

context