Contents

2023 05 31 设计模式之单例模式

什么是单例模式

单例模式(Singleton Pattern)是最常用的设计模式之一,它保证一个类只有一个实例,并提供一个全局访问点。

在实际开发中,单例模式常用于:

  • 数据库连接池
  • 线程池
  • 配置管理器
  • 日志记录器

实现方式

1. 饿汉式(线程安全)

类加载时就立即创建实例,由JVM保证线程安全。

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

优点:实现简单,无线程安全问题

缺点:类加载时就创建,可能造成内存浪费

2. 懒汉式(线程不安全)

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

缺点:线程不安全,多线程下可能创建多个实例

3. 懒汉式 + synchronized(线程安全)

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

缺点:每次调用都要同步,性能较差

4. 双重检查锁(推荐)

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 关键字,防止指令重排

5. 静态内部类(推荐)

public class Singleton {
    private Singleton() {}
    
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

优点:延迟加载,线程安全,推荐使用

6. 枚举方式(最佳)

public enum Singleton {
    INSTANCE;
    
    public void doSomething() {
        // 业务逻辑
    }
}

优点

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

对比总结

方式线程安全延迟加载防止反射防止反序列化
饿汉式
懒汉式
synchronized
双重检查锁
静态内部类
枚举

实际应用

在Spring框架中,Bean的默认作用域就是单例(Singleton),容器启动时创建所有单例Bean。

@Component
public class MyBean {
    // 默认是单例作用域
}

如果需要多例,可以使用 @Scope("prototype") 注解。

总结

  • 简单场景推荐使用静态内部类枚举
  • 需要延迟加载时选择双重检查锁静态内部类
  • 需要防止反射和反序列化时使用枚举
  • Spring等框架已经内置了单例管理,无需手动实现