单例类(Singleton)

单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类在系统中只有一个实例,并提供一个全局访问点。

单例模式的特点

  1. 唯一性:整个系统中该类只有一个实例。
  2. 全局访问:可以在整个程序中访问这个实例。
  3. 延迟初始化(可选):只有在真正需要时才创建实例,提高性能。

单例模式的实现方式

1. 饿汉式(Eager Singleton)

实现方式

  • 直接在类加载时创建实例,线程安全。
  • 缺点是可能会造成资源浪费(如果实例很早创建,但一直未被使用)。
1
2
3
4
5
6
7
8
9
public class Singleton {
private static final Singleton INSTANCE = new Singleton(); // 直接创建实例

private Singleton() {} // 私有构造方法,防止外部创建对象

public static Singleton getInstance() {
return INSTANCE;
}
}

2. 懒汉式(Lazy Singleton)

实现方式

  • 只有在第一次调用 getInstance() 时才创建实例,避免资源浪费。
  • 非线程安全,多个线程可能同时创建多个实例。
1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static Singleton instance;

private Singleton() {} // 私有构造方法

public static Singleton getInstance() {
if (instance == null) { // 只有在需要时才创建实例
instance = new Singleton();
}
return instance;
}
}

问题:多线程环境下,可能出现多个实例。


3. 线程安全的懒汉式

方式 1:使用 synchronized

1
2
3
4
5
6
7
8
9
10
11
12
public class Singleton {
private static Singleton instance;

private Singleton() {}

public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}

缺点synchronized 影响性能,每次访问都需要加锁。


方式 2:双重检查锁(Double-Check Locking)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
private static volatile Singleton instance;

private Singleton() {}

public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
  • volatile 关键字:防止指令重排,保证可见性。
  • 双重检查:避免不必要的加锁,提高性能。

4. 静态内部类(推荐方式)

1
2
3
4
5
6
7
8
9
10
11
public class Singleton {
private Singleton() {}

private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}

public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

优点

  • 线程安全,利用类加载机制确保实例唯一。
  • 延迟加载(Lazy Loading),只有 getInstance() 被调用时,才创建实例。

5. 枚举实现单例(最佳方案)

1
2
3
4
5
6
7
public enum Singleton {
INSTANCE;

public void doSomething() {
System.out.println("Hello Singleton!");
}
}

优点

  • 线程安全,由 JVM 保证。
  • 防止反射攻击(私有构造方法仍可通过反射破坏单例,但枚举不会)。
  • 防止反序列化创建新实例

防止反射和序列化破坏单例

  1. 防止反射破坏单例

    • 在构造方法中检测实例是否已存在:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {
    if (instance != null) {
    throw new RuntimeException("禁止反射创建单例!");
    }
    }

    public static Singleton getInstance() {
    if (instance == null) {
    synchronized (Singleton.class) {
    if (instance == null) {
    instance = new Singleton();
    }
    }
    }
    return instance;
    }
    }
  2. 防止序列化破坏单例

    • 通过 readResolve() 方法确保反序列化返回同一个实例:
    1
    2
    3
    private Object readResolve() {
    return instance;
    }

总结

方式 是否线程安全 是否延迟加载 是否推荐
饿汉式 ❌(可能浪费资源)
懒汉式 ❌(非线程安全)
synchronized 懒汉式 ❌(影响性能)
双重检查锁
静态内部类 ✅(推荐)
枚举 ⭐(最佳方式)

最佳实践:优先使用 枚举静态内部类 实现单例。