Concurrency Patternshigh

Immutable Objects

An immutable object's state cannot be modified after construction. Immutable objects are inherently thread-safe — no synchronization is needed because state cannot change.

Memory anchor

Immutable Objects = a printed book. Once published, nobody can change the words on the page. Want a revised edition? Print a whole new book. Ten people can read the same copy simultaneously without stepping on each other.

Expected depth

Requirements for immutability in Java: (1) declare class final (prevent subclass mutability); (2) all fields private final; (3) no setters; (4) deep immutability for mutable fields — return defensive copies, store copies of mutable inputs. Use Builder for complex construction. Examples: String, Integer, Java records, Google Guava ImmutableList.

Deep — senior internals

Immutability eliminates an entire class of concurrency bugs: no visibility issues (fields are written once before publication, which establishes happens-before with a safe publication pattern), no atomicity issues (state is consistent by definition), no ordering issues (no mutation to order). The cost is that every 'mutation' creates a new object — this is acceptable for small objects but expensive for large data structures. Persistent data structures (Clojure, Scala's immutable collections) solve this with structural sharing: a new version of a collection shares the unchanged parts with the old version, making 'copy' O(log n) instead of O(n). In domain modeling, immutability is natural for Value Objects (Money, Email, PhoneNumber) — two Money objects with the same amount and currency are equal regardless of identity.

🎤Interview-ready answer

Immutable objects are my first line of defense in concurrent code. A Money value object is final, with private final long cents and private final Currency currency — no setters, defensive copies in getters that return mutable types, and final class to prevent mutable subclasses. Two threads can share a Money object freely — no locking needed. I use Builder pattern to construct complex immutable objects. The trade-off I always raise: every update creates a new object, so for high-mutation rate scenarios (counters, accumulators), use AtomicLong or similar mutable thread-safe types instead.

Common trap

Shallow immutability is not true immutability. A class with all final fields can still be mutable if one of those fields is a reference to a mutable object (e.g., final List<String> items — items is final, but items.add() still mutates state). Deep immutability requires all reachable state to be immutable.

Related concepts