Why are immutable objects thread-safe?
Immutable objects are thread-safe because a race condition requires at least one write and one read. If there are no writes after creation — a race condition is physically impos...
Basic Level
Immutable objects are thread-safe because a race condition requires at least one write and one read. If there are no writes after creation — a race condition is physically impossible.
public final class Config {
private final String url;
private final int timeout;
public Config(String url, int timeout) {
this.url = url;
this.timeout = timeout;
}
public String getUrl() { return url; }
public int getTimeout() { return timeout; }
// No setters — no one can change the state
}
This object can be safely passed between threads — no need for synchronized or Lock.
Important: immutability ≠ thread-safe for references
An immutable object is safe for reading from many threads. But if its field references a mutable object — that object needs to be protected separately.
Intermediate Level
Absence of Race Conditions
A Race Condition occurs when threads simultaneously read and write the same data. An immutable object doesn’t allow writes — write conflicts are physically impossible.
Java Memory Model guarantees for final fields
Even if the object doesn’t change, there’s a risk of seeing it in a partially initialized state. The final keyword prevents this:
- At the end of the constructor, a “freeze” of all
finalfields occurs - Any thread that receives a reference to the object after the constructor completes will see all
finalfields initialized - The processor and compiler cannot reorder instructions so that the reference becomes available before
finalfields are set
Copy-on-“modification”
public final class Counter {
private final int value;
public Counter(int value) { this.value = value; }
public Counter increment() {
return new Counter(this.value + 1); // new object
}
}
Advanced Level
Freeze Semantics and Memory Barriers
The JLS (Java Language Specification) §17.5 guarantees: after the constructor completes, all final fields are visible to any thread. This is ensured by a memory barrier at the end of the constructor, which:
- Prohibits reordering of instructions beyond the barrier
- Guarantees visibility of all
finalfields - Creates safe publication without
volatileor synchronization
Object vs Reference
It’s important to distinguish the immutability of the object itself from the immutability of the reference to it:
public class Service {
private ImmutableObject data = new ImmutableObject("v1"); // The reference is mutable!
public void update(String val) {
this.data = new ImmutableObject(val); // Not thread-safe without volatile/final
}
}
The ImmutableObject itself is thread-safe, but the data reference is not. Other threads may see the old reference from their cache.
Usage patterns
- Copy-On-Write —
CopyOnWriteArrayListrelies on array immutability - Value Objects in DDD — immutable objects for data transfer between layers
- Actors / Event Sourcing — each event is immutable, state is recalculated
Summary for Advanced
- Thread-safety = no mutation + JMM guarantees for
finalfields - Immutability eliminates synchronization overhead — lock-free access eliminates contention between threads, which under high contention can reduce throughput by 50-90%.
- Always use
finalfor publishing immutable objects - For mutable references to immutable objects, use
volatileorAtomicReference
Interview Cheat Sheet
Must know:
- Race condition impossible — no writes after object creation
finalfields ensure safe publication — JLS §17.5, freeze at end of constructor- Memory barrier at the end of constructor prohibits instruction reordering
- Immutability ≠ thread-safe for references: the reference to the object may be mutable
- Copy-On-Write pattern —
CopyOnWriteArrayListrelies on array immutability - For mutable references:
volatileorAtomicReference
Frequent follow-up questions:
- Why is synchronized not needed? — No writes = no data competition
- What is freeze in JMM? — Memory barrier at end of constructor, guaranteeing final field visibility
- Can a thread see a partially created object? — Without final fields — yes; with final — no (safe publication)
- What if the reference to an immutable object is mutable? — Need
volatileorAtomicReferencefor safe replacement
Red flags (DO NOT say):
- “All objects without setters are thread-safe” — mutable fields inside are still dangerous
- “synchronized makes an object immutable” — that’s about synchronization, not immutability
- “final fields can be changed via reflection” — in Java 16+ this is blocked for java.base
- “Immutable object = thread-safe reference” — the reference itself may be mutable
Related topics:
- [[1. What is an immutable object]]
- [[2. What advantages do immutable objects provide]]
- [[7. What is the final keyword and how does it help create immutable classes]]
- [[23. How does immutability affect performance]]