Back to ThreadLocal & Thread Confinement
Q5Question 5 of 6

What is the this-escaping problem?

Key Concepts
  • This escape occurs when a reference to an object becomes visible to another thread before the constructor finishes.
  • Common causes: registering this as a listener, starting a thread, or passing this to external code during construction.
  • Another thread may observe fields with default values (null, 0) or even partially written final fields.
  • The JMM only guarantees safe publication for fully and correctly constructed objects.
  • Fix: private constructor plus static factory method that registers after full construction.
  • Fix: Spring @PostConstruct for listener registration after bean construction.
  • Rule: do not publish this until the constructor has returned.

Definition

Never let `this` leave the constructor: publish the object only after it is fully built, using a static factory or a separate init step.
  • This escape – a reference to the object becomes visible to another thread before the constructor finishes.
  • Causes – registering this as a listener, starting a thread from the constructor, passing this to external code.
  • Danger – other threads see default field values (null, 0) or partially written final fields.
  • JMM rule – safe publication is guaranteed only for fully and correctly constructed objects.
  • Fix – private constructor plus static factory method, or @PostConstruct for Spring beans.

A new bank account form for Rajesh is being filled in. The clerk hands out a photocopy before finishing. The auditor reads it and sees a blank balance and no account number, then makes a wrong decision. The safe process: finish the entire form, then hand out copies.

Understanding the Concepts

How does this escape through listener registration?

When you register this as a listener inside a constructor, the listener callback can be invoked by another thread before the constructor finishes initializing all fields.

JAVA
package com.bank.monitor;

import java.math.BigDecimal;

// WRONG: this escapes during construction
public class SettlementMonitor {
    private final String bankName;
    private final BigDecimal threshold;

    public SettlementMonitor(EventSource source) {
        source.registerListener(this);  // THIS ESCAPES!
        // Another thread can now call back into this object
        this.bankName = "National Bank of India";
        this.threshold = new BigDecimal("100000");
        // The callback thread may see bankName=null, threshold=null
    }
}

How does this escape through thread start?

Starting a thread from a constructor passes this (via a method reference or inner class) to the new thread. The new thread can begin executing before the constructor finishes.

JAVA
// WRONG: thread can run before constructor finishes
public class Worker {
    private final int batchSize;

    public Worker() {
        new Thread(this::process).start();  // THIS ESCAPES via method reference!
        this.batchSize = 100;
        // The thread may read batchSize as 0 (default value) instead of 100
    }

    private void process() {
        System.out.println("Batch size: " + batchSize);  // may print 0!
    }
}

How does this escape through inner class capture?

An anonymous inner class or lambda capturing this also publishes the reference to any thread that receives the inner class instance.

JAVA
// WRONG: this leaks via inner class capture
public class AccountService {
    private final BigDecimal minBalance;

    public AccountService(Executor executor) {
        this.minBalance = new BigDecimal("1000");
        executor.execute(new Runnable() {
            public void run() {
                // Inner class captures enclosing AccountService.this
                System.out.println(AccountService.this.minBalance);
                // May see null if constructor has not finished
            }
        });
    }
}

How do you fix it with a static factory?

The fix is a private constructor plus a public static factory method. The factory first constructs the object completely, then registers it.

JAVA
package com.bank.monitor;

import java.math.BigDecimal;

public class SettlementMonitor {
    private final String bankName;
    private final BigDecimal threshold;

    private SettlementMonitor() {
        this.bankName = "National Bank of India";
        this.threshold = new BigDecimal("100000");
        // Construction is complete before any external code sees this
    }

    public static SettlementMonitor create(EventSource source) {
        SettlementMonitor monitor = new SettlementMonitor();  // fully built
        source.registerListener(monitor);  // safe: all fields initialized
        return monitor;
    }
}

How do you fix it in Spring?

Spring beans are constructed first, then @Autowired fields are injected, and finally @PostConstruct methods run. Registering listeners in @PostConstruct is safe because the bean is fully initialized.

JAVA
@Component
public class SettlementMonitor {

    private final String bankName = "National Bank of India";
    private final BigDecimal threshold = new BigDecimal("100000");

    @Autowired
    private EventSource source;

    @PostConstruct
    public void init() {
        source.registerListener(this);  // safe: bean is fully constructed
    }
}

How do final field semantics relate to this escape?

The Java Memory Model guarantees that final fields are visible to other threads after the constructor completes. But if this escapes during construction, other threads can see the object before the constructor finishes, and the final field guarantee does not hold. The JMM explicitly states that this guarantee applies only to properly constructed objects.

What fields are visible during this escape?

| Field type | Visible value during escape |
|------------|----------------------------|
| int, long, double | 0 (default) |
| boolean | false |
| Object references | null |
| final fields | Possibly default (JMM guarantee broken) |

What are common mistakes?

Passing this to any external code during construction. This includes registering listeners, starting threads, publishing to static maps, or calling overridable methods (which may be overridden in a subclass whose constructor has not yet run).

What are common interview follow-up questions?

  • Can an object be safely published if it escapes during construction? No. The JMM only guarantees safe publication for fully and correctly constructed objects.
  • How does the JMM treat an object whose this escaped? Other threads may observe a partially initialized state with default field values.
  • Does calling an overridable method in the constructor count as this escape? Yes, because a subclass override may pass this to external code or access subclass fields that are not yet initialized.

Visual Overview

DIAGRAM
  This Escape:
  ┌──────────────────────────────────────┐
  │ Constructor running...                │
  │   source.register(this) ──────────▶ Another thread sees object
  │   this.bankName = "NBI"              with bankName = null
  │   this.threshold = 100000            and threshold = 0
  └──────────────────────────────────────┘

  Safe Factory:
  ┌──────────────────────────────────────┐
  │ 1. private SettlementMonitor()       │
  │    bankName = "NBI"  ✓               │
  │    threshold = 100000  ✓             │
  │ 2. source.register(monitor)          │
  │    Object is fully built  ✓          │
  └──────────────────────────────────────┘

Key Takeaway

The this escape problem happens when a reference to an object becomes visible before the constructor finishes. The danger is that another thread sees null references and zero numbers. The JMM only promises safe publication for objects that are fully constructed.

The fix: never let this escape the constructor. Use a private constructor plus a static factory, or Spring's @PostConstruct.

Remember one sentence: do not publish this during construction, because other threads can observe a partially initialized object; use a private constructor with a static factory or post-construction initialization.

---

Share
Use ← → arrow keys to navigate