Immutability & Thread Safety

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • An Immutable object is an object whose internal state cannot be modified after it is constructed.
  • Immutable objects are inherently thread-safe.
  • Because their state never changes, there can be no race conditions, eliminating the need for synchronized blocks.

Concept

Concurrency bugs only happen when two threads try to modify the exact same piece of data at the same time. If a piece of data is entirely read-only (immutable), a million threads can read it simultaneously without any locks, and the program will function perfectly with maximum performance.

The most famous immutable class in Java is String. When you do str = str + "!", you are not mutating the original string; you are creating a brand new String object.

How to make a class Immutable:

  1. Declare the class as final (so it can’t be extended and overridden).
  2. Make all fields private and final.
  3. Do not provide any setter methods.
  4. If a field is a mutable object (like a Date or List), defensively copy it during construction, and return defensive copies in the getters.

Examples

import java.util.ArrayList;
import java.util.List;

// 1. Class is final
public final class ImmutableStudent {
    
    // 2. Fields are private final
    private final String name;
    private final List<String> courses;
    
    public ImmutableStudent(String name, List<String> courses) {
        this.name = name;
        // 4. Defensive copy on creation! 
        // Prevents caller from modifying the list after passing it in.
        this.courses = new ArrayList<>(courses); 
    }
    
    // 3. No Setters!
    
    public String getName() {
        return name; // Strings are immutable, safe to return directly
    }
    
    public List<String> getCourses() {
        // 4. Defensive copy on getter!
        // Prevents caller from doing student.getCourses().add("Hacking 101");
        return new ArrayList<>(courses);
    }
}

Interview Questions

Q: Why must you perform defensive copying for mutable fields in an immutable class?
A: If your class has a private final Date dob; field, the final keyword only protects the reference to the Date object; it does not protect the Date object’s internal data. If you write public Date getDob() { return dob; }, another thread can call getDob().setYear(2099). Your “immutable” object’s state just changed! You must return a copy: return new Date(dob.getTime());.

Q: Is final enough to guarantee thread safety?
A: No. While final guarantees that the reference cannot point to a new object, if the referenced object itself is mutable (like a HashMap), threads can still concurrently modify its internal contents, causing race conditions. You need complete, deep immutability.