In the Java Memory Model, what special visibility guarantee applies to an instance field declared `final`, and what must the reading thread do to obtain it?
answer
- freeze at end of constructor
- reader needs no synchronization
- dereference chain also frozen
- `this` must not escape
- publication, not mutation, guarantee
basics
~20 sFinal fields are frozen when the constructor returns. Any thread that later obtains the object's reference sees those fields correctly initialized, with no lock and no volatile on the reader's side, provided the reference did not escape during construction.
solid answer
~50 sThe memory model defines a **freeze action** on every final field at the end of the constructor. A thread that obtains a reference to the object *after* construction completed is guaranteed to see the frozen values, and to see versions of anything reachable through those final fields that are at least as up to date as the final fields were at freeze time. This is the only ordering guarantee in the model that demands **nothing from the reader**: no `synchronized`, no `volatile` read, no atomic. It is why `String` or a class with only final fields can be handed to another thread through a plain field or a plain `HashMap` and still be observed fully built. The condition sits on the writer side: the object must not publish `this` before the constructor finishes. If another thread can reach the object early, it may observe the final fields at default values (`0`, `null`), and the guarantee is void.
code
java · 16 linesfinal class Holder {
final int guaranteed; // frozen at end of constructor
int plain; // no guarantee for other threads
Holder(int v) {
this.guaranteed = v;
this.plain = v;
}
}
// Thread A: shared = new Holder(42); // plain, racy write of the reference
// Thread B: Holder h = shared;
// if (h != null) {
// h.guaranteed // always 42
// h.plain // may legally be 0
// }go deeper
Recall the headline: fields marked final are set once in the constructor and other threads that get the object afterwards see them properly set, without extra synchronization.
State the freeze-at-end-of-constructor rule, that the reader needs no synchronization, and the this-must-not-escape condition. Contrast with a non-final field in the same class.
Add the dereference-chain part (state reachable through a final field is covered), the racy-publication case, and the boundary where the guarantee stops at mutable contents or post-construction reflective writes.
Frame it as the one asymmetric, reader-free guarantee in the model, explain what it buys design-wise (immutable objects shareable through any channel), and name the invariants a codebase must hold to keep it — no this escape, no post-construction mutation, no exposed mutable internals.
## What goes wrong without it Ordinary field writes carry no cross-thread ordering. If thread A builds an object and stores its reference into a plain, non-volatile field, thread B reading that field may legally see the reference but observe the object's fields at their **default values** (`0`, `false`, `null`). Both the JIT and the hardware are free to make the store of the reference visible before the stores that filled in the fields, because within thread A nothing depends on the order. The memory model carves out one exception to that rule, and it applies to fields declared `final`. ## Freeze actions The specification introduces an action that exists nowhere in the source code: at the **end of the constructor** in which a final field is set, a *freeze* of that field occurs. The ordering rule is then stated over the freeze rather than over the assignment: if a thread obtains a reference to the object only through a chain of reads that begins after the freeze, that thread is guaranteed to read the frozen value, not the default value. Two details of that phrasing matter. 1. **The reader does nothing.** Every other guarantee in the model is a pair: a release on one side, an acquire on the other. Frozen fields are one-sided. The publishing thread pays a small cost at the end of the constructor; the reading thread pays nothing, holds no lock, and does not have to read anything `volatile`. Even a *racy* publication — a plain write of the reference that another thread happens to read — still delivers the correct final-field values. 2. **The guarantee extends one step outward.** The spec says a reader also sees versions of any object or array referenced by a final field that are **at least as up to date** as the final field was when frozen. So a final field pointing at an array whose elements were filled before the constructor ended gives you the filled elements, not a zeroed array. This is often called the *dereference chain* or deep-freeze part of the rule, and it is what makes `String` — whose character array is written during construction — safe to share. ## The condition: no early publication of `this` The guarantee is stated over references *obtained after* the constructor completes. If the constructor leaks `this` — registers a listener, starts a thread, stores itself in a static registry, calls an overridable method that leaks it — another thread can hold the reference before the freeze happens. That reference was not obtained after the freeze, so the model gives it nothing: the other thread may read `0` or `null` from a field the constructor already assigned, and may even see the field change value under it. ## Where the guarantee stops It guarantees the *value of the field*. If that value is a reference to a mutable object, only the reference and the state it had at freeze time are covered; later mutations by other threads are ordinary racy writes with no guarantee at all. Frozen fields give you safe **publication**, not thread safety of whatever you published. The rule also assumes the field is genuinely never modified after construction. Deserialization frameworks and reflection can write final fields after the freeze; the spec explicitly says that a thread which read the field before such a modification is not required to observe the change, and the guarantee becomes murky. Treat post-construction mutation of a final field as being outside the model. ## Practical consequence A class whose fields are all final, whose constructor does not leak `this`, and which does not expose mutable internals can be published across threads by any means at all — a plain field, a non-thread-safe map, a data race — and every reader that sees the reference sees a fully built object. That property is a feature of the *specification*, not of any particular collector or CPU, so it holds on every conforming JVM.
- Does the guarantee still hold if the reference itself is published through a data race, for example a plain non-volatile static field?Yes. That is precisely what makes the rule special. The guarantee is written over the freeze and the reference-obtaining read, not over any synchronization between the threads. As long as the reference was written after the constructor completed and the reader gets it from that write, the final fields read correctly — even though the reader may or may not see the reference at all, and non-final fields of the same object are still unguaranteed.
- An object has one final field pointing to an `int[]` filled inside the constructor. What does a reader see?It sees the array reference and elements at least as up to date as they were when the field was frozen, so the values written in the constructor are visible. The guarantee covers the state reachable through the final field at freeze time. Any element written to that array *after* the constructor returned is an ordinary racy write with no guarantee.
- What breaks the guarantee even when every field is declared final?Publishing `this` before the constructor completes. Starting a thread that captures `this`, registering a callback, or storing the instance in a static registry from inside the constructor all let another thread obtain the reference before the freeze, so that reference is not covered and the fields may be observed at default values.
A freeze is like sealing a shipping crate at the loading dock: whoever receives the crate afterwards is guaranteed to find everything that was inside when it was sealed, without having to coordinate with the packer. If you hand out the crate's address before you seal it, that promise is off.
saying these in an interview costs you the question
- Claiming `final` makes the object or its contents immutable or thread-safe, rather than guaranteeing publication of the field value.
- Saying the reader must still use `volatile` or a lock to see final fields.
- Believing the guarantee holds for non-final fields set in the same constructor.
- Thinking the guarantee survives a constructor that leaks `this`.
- Asserting `final` inserts a lock or makes reads more expensive for the reader.