一、单例模式概述
单例模式确保一个类只有一个实例,并提供全局访问点。
适用场景:
- 需要频繁创建销毁的对象
- 创建耗时或耗资源的对象
- 工具类对象
- 配置类对象
二、饿汉式
类加载时就创建实例。
静态常量
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()操作分为三步:
- 分配内存空间
- 初始化对象
- 将引用指向内存地址
指令重排序可能导致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; } }
|
八、各实现方式对比
| 方式 |
懒加载 |
线程安全 |
防反射 |
防序列化 |
性能 |
| 饿汉式 |
否 |
是 |
否 |
否 |
好 |
| 懒汉式(同步) |
是 |
是 |
否 |
否 |
差 |
| 双重检查锁 |
是 |
是 |
否 |
否 |
好 |
| 静态内部类 |
是 |
是 |
否 |
否 |
好 |
| 枚举 |
否 |
是 |
是 |
是 |
好 |
九、总结
推荐使用顺序:
- 枚举:最安全,Effective Java推荐
- 静态内部类:懒加载,线程安全
- 双重检查锁:需要延迟加载且对性能有要求
单例模式是最简单的设计模式之一,但实现一个完全安全的单例需要考虑线程安全、反射攻击和序列化问题。