Fundamentals 7 min read

Java final Visibility: JMM Freeze, This Escape, and Lock-Free Guarantees

This article explains how Java's final keyword ensures cross-thread visibility of final fields through the JMM's freeze action at constructor end, provided the this reference does not escape during construction, demonstrating with a code example that shows a race condition when this escapes and the correct behavior when final fields are properly frozen.

samdeepthink
samdeepthink
samdeepthink
Java final Visibility: JMM Freeze, This Escape, and Lock-Free Guarantees

Introduction: final Beyond Immutability

Most developers know final as a modifier that makes variables assign-once, prevents method overriding, or blocks class inheritance. However, in the Java Memory Model (JMM), final has a crucial concurrency role: it guarantees visibility of final fields across threads without requiring volatile or locks — provided the this reference does not escape during construction.

What Is "This Escape"?

Escape means an object is passed to another thread before its constructor finishes. The article illustrates this with a runnable example on Windows 11 using Java 17.

Code Example: Demonstrating This Escape

public class ThisEscape {
    static Holder holder;

    static class Holder {
        int x;

        Holder() {
            // this escapes here, before x is initialized
            holder = this;
            // Simulate remaining initialization work
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {}
            // x assigned last
            this.x = 42;
        }
    }

    public static void main(String[] args) throws Exception {
        Thread writer = new Thread(() -> new Holder());
        Thread reader = new Thread(() -> {
            while (holder == null) {
                try { Thread.sleep(10); } catch (InterruptedException e) {}
            }
            System.out.println("during construction: x = " + holder.x);
        });

        writer.start();
        reader.start();
        writer.join();
        reader.join();
        System.out.println("after construction: x = " + holder.x);
    }
}

Execution Results

during construction: x = 0
after construction: x = 42
during construction: x = 0
after construction: x = 42
during construction: x = 0
after construction: x = 42

Three runs produce identical output: during construction the reading thread sees x = 0 (the default int value), while after construction it sees x = 42.

Timeline Analysis

Constructor thread enters Holder(), first line executes holder = this — the partially initialized object reference is published.

Constructor sleeps for 1 second; initialization pauses.

Reader thread polls holder, finds it non-null, and immediately reads holder.x.

At this moment x is still 0 because this.x = 42 has not executed.

After 1 second, constructor resumes, assigns x = 42, and finishes.

Subsequent reads see 42.

The root cause: this escaped mid-construction, allowing another thread to observe a "half-initialized" object. This is called this escape .

JMM Freeze Action for final Fields

The JMM defines a freeze action for each final field. This action occurs at the end of the constructor that assigns the field. In the example, if x were declared final:

Constructor writes this.x = 42.

At the instant the constructor returns, JMM performs a freeze: the fact x = 42 is "sealed".

Thereafter, any thread that obtains a reference to the object and reads x is guaranteed to see 42 — never 0 or any intermediate value.

Critical precondition: this must not escape. Because freeze happens at constructor end, if this is handed out earlier, a reading thread can access the field before freeze occurs, seeing the default value.

Conclusion

The opening statement distills to: final provides cross-thread visibility for final fields without volatile or synchronization, contingent on this not escaping during construction. This is a foundational JMM guarantee that enables safe publication of immutable objects.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaConcurrencyJMMJava Memory Modelvisibilityfinal keywordfreeze actionthis escape
samdeepthink
Written by

samdeepthink

Knowledge Planet: Old Dock's Tech Chronicles Zhihu: SamDeepThinking A technical manager who still codes heavily on the front line. From junior developer to tech lead, then tech manager, now leading the whole front‑ and back‑end development team—leveling up along the way. I have some insights on programming, career development, and tech management.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.