Fundamentals 10 min read

Java Singleton Pattern: Eager, Lazy, Double‑Check Locking, Holder, and Enum Implementations

This article explains several common Java singleton implementations—including lazy ("hungry"), eager, double‑checked locking, holder, and enum patterns—detailing their code, thread‑safety characteristics, performance trade‑offs, and pitfalls such as partial initialization and reflection attacks.

Top Architecture Tech Stack
Top Architecture Tech Stack
Top Architecture Tech Stack
Java Singleton Pattern: Eager, Lazy, Double‑Check Locking, Holder, and Enum Implementations

Understanding different ways to implement the singleton pattern is useful for interviews, as it tests knowledge of design patterns, coding style, and the ability to discuss related topics.

The article groups singleton implementations into four main categories and examines each with code examples and analysis.

Lazy ("hungry") singleton (also called "饱汉" in the original text) delays instance creation until the first call to getInstance() , achieving lazy loading. The basic version is not thread‑safe:

// 饱汉
// UnThreadSafe
public class Singleton1 {
  private static Singleton1 singleton = null;
  private Singleton1() {}
  public static Singleton1 getInstance() {
    if (singleton == null) {
      singleton = new Singleton1();
    }
    return singleton;
  }
}

Variant 1 adds the synchronized keyword to the method, making it thread‑safe but serializing all accesses, which hurts performance.

// 饱汉
// ThreadSafe
public class Singleton1_1 {
  private static Singleton1_1 singleton = null;
  private Singleton1_1() {}
  public synchronized static Singleton1_1 getInstance() {
    if (singleton == null) {
      singleton = new Singleton1_1();
    }
    return singleton;
  }
}

Variant 2 implements the classic double‑checked locking (DCL 1.0). It attempts to reduce synchronization overhead but remains unsafe on some JVMs because of instruction reordering, which can produce a partially constructed object.

// 饱汉
// UnThreadSafe
public class Singleton1_2 {
  private static Singleton1_2 singleton = null;
  public int f1 = 1;   // trigger partial‑init issue
  public int f2 = 2;
  private Singleton1_2() {}
  public static Singleton1_2 getInstance() {
    // may get half object
    if (singleton == null) {
      synchronized (Singleton1_2.class) {
        if (singleton == null) {
          singleton = new Singleton1_2();
        }
      }
    }
    return singleton;
  }
}

Variant 3 (DCL 2.0) fixes the above problem by declaring the instance variable as volatile , which prevents reordering and guarantees visibility, making it suitable for performance‑sensitive multithreaded environments.

// 饱汉
// ThreadSafe
public class Singleton1_3 {
  private static volatile Singleton1_3 singleton = null;
  public int f1 = 1;   // trigger partial‑init issue
  public int f2 = 2;
  private Singleton1_3() {}
  public static Singleton1_3 getInstance() {
    if (singleton == null) {
      synchronized (Singleton1_3.class) {
        // must be a complete instance
        if (singleton == null) {
          singleton = new Singleton1_3();
        }
      }
    }
    return singleton;
  }
}

Eager singleton ("饿汉") creates the instance when the class is loaded, which is inherently thread‑safe but may waste resources if the instance is never used.

// 饿汉
// ThreadSafe
public class Singleton2 {
  private static final Singleton2 singleton = new Singleton2();
  private Singleton2() {}
  public static Singleton2 getInstance() {
    return singleton;
  }
}

The Holder pattern combines the advantages of eager initialization (thread safety) with lazy loading by using a static inner class to hold the instance.

// Holder模式
// ThreadSafe
public class Singleton3 {
  private static class SingletonHolder {
    private static final Singleton3 singleton = new Singleton3();
    private SingletonHolder() {}
  }
  private Singleton3() {}
  public static Singleton3 getInstance() {
    return SingletonHolder.singleton;
  }
}

The enum singleton leverages Java's enum type to provide a concise, serialization‑safe, and reflection‑proof singleton implementation.

// 枚举
// ThreadSafe
public enum Singleton4 {
  SINGLETON;
}

Enums can also carry state and behavior; the article shows an advanced enum singleton with a private field, a method, and an abstract method implementation.

// 枚举
// ThreadSafe
public enum Singleton4_1 {
  SINGLETON("enum is the easiest singleton pattern, but not the most readable") {
    public void testAbsMethod() {
      print();
      System.out.println("enum is ugly, but so flexible to make lots of trick");
    }
  };
  private String comment = null;
  Singleton4_1(String comment) {
    this.comment = comment;
  }
  public void print() {
    System.out.println("comment=" + comment);
  }
  abstract public void testAbsMethod();
  public static Singleton4_1 getInstance() {
    return SINGLETON;
  }
}

Finally, the article notes that reflection and serialization can break most singleton implementations, while the enum approach naturally resists these attacks. The author concludes that singleton patterns are frequent interview topics and relatively easy to implement.

JavaEnumsingletonDesignPatternLazyInitializationThreadSafety
Top Architecture Tech Stack
Written by

Top Architecture Tech Stack

Sharing Java and Python tech insights, with occasional practical development tool tips.

0 followers
Reader feedback

How this landed with the community

login 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.