一、单例模式概述

单例模式确保一个类只有一个实例,并提供全局访问点。

适用场景:

  • 需要频繁创建销毁的对象
  • 创建耗时或耗资源的对象
  • 工具类对象
  • 配置类对象

二、饿汉式

类加载时就创建实例。

静态常量

1
2
3
4
5
6
7
8
9
10
public class Singleton {

private static final Singleton INSTANCE = new Singleton();

private Singleton() {}

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

优点:实现简单,线程安全。
缺点:类加载时就初始化,可能造成资源浪费。

静态代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Singleton {

private static final Singleton INSTANCE;

static {
INSTANCE = new Singleton();
}

private Singleton() {}

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

适合需要在初始化时处理异常的场景。

三、懒汉式

使用时才创建实例。

线程不安全

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {

private static Singleton instance;

private Singleton() {}

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

问题:多线程下可能创建多个实例。

同步方法

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Singleton {

private static Singleton instance;

private Singleton() {}

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

问题:每次获取实例都加锁,性能差。

同步代码块(错误方式)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton {

private static Singleton instance;

private Singleton() {}

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

问题:仍然存在线程安全问题。

四、双重检查锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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禁止指令重排序
  • 两次检查避免重复创建
  • 锁粒度小,性能好

为什么要用volatile

new Singleton()操作分为三步:

  1. 分配内存空间
  2. 初始化对象
  3. 将引用指向内存地址

指令重排序可能导致2和3的顺序交换,其他线程可能获取到未初始化的对象。

五、静态内部类

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

private Singleton() {}

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

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

原理:

  • 外部类加载时不会立即加载内部类
  • 调用getInstance时才加载内部类
  • 类加载机制保证线程安全

优点:

  • 懒加载
  • 线程安全
  • 无锁

六、枚举

1
2
3
4
5
6
7
8
public enum Singleton {

INSTANCE;

public void doSomething() {
// 业务逻辑
}
}

使用:

1
Singleton.INSTANCE.doSomething();

优点:

  • 线程安全
  • 防止反序列化重新创建对象
  • 防止反射攻击

Effective Java推荐的方式。

七、防止破坏单例

防止反射攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
}
}

防止序列化破坏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Singleton implements Serializable {

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;
}

// 防止反序列化创建新对象
private Object readResolve() {
return instance;
}
}

八、各实现方式对比

方式 懒加载 线程安全 防反射 防序列化 性能
饿汉式
懒汉式(同步)
双重检查锁
静态内部类
枚举

九、总结

推荐使用顺序:

  1. 枚举:最安全,Effective Java推荐
  2. 静态内部类:懒加载,线程安全
  3. 双重检查锁:需要延迟加载且对性能有要求

单例模式是最简单的设计模式之一,但实现一个完全安全的单例需要考虑线程安全、反射攻击和序列化问题。