单例模式(Singleton Pattern)
单例模式是一种 创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。
原理
- 核心思想:控制实例化过程,确保一个类只能创建一个实例。
- 实现方式:
- 将构造方法设置为私有。
- 提供一个静态方法返回唯一实例。
- 参与角色:
- 单例类(Singleton):负责自己唯一实例的创建和全局访问。
- 客户端(Client):通过单例类的访问方法获取实例。
优点
- 节省资源:单例对象可以被多次重复使用,尤其是资源密集型对象(如数据库连接)。
- 全局访问点:提供一个全局共享对象,便于在系统中访问。
- 控制实例数量:确保系统中只有一个对象实例,避免重复实例化问题。
缺点
- 违背单一职责原则:单例类同时负责实例控制和自身功能。
- 并发问题:需要处理多线程场景中的并发访问。
- 难以扩展:单例模式难以被继承和测试。
单例模式实现方式
1. 饿汉式(线程安全)
- 实例在类加载时创建,线程安全。
- 缺点:如果实例未被使用,会造成内存浪费。
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. 双重检查锁(线程安全)
- 结合懒汉式和线程安全的优点,推荐使用。
public class Singleton {
// 使用 volatile 保证可见性和指令重排序的禁止
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;
}
}
4. 静态内部类(线程安全)
- 利用类加载机制实现延迟加载,且线程安全。
public class Singleton {
private Singleton() {}
// 静态内部类持有单例实例
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
5. 枚举实现(推荐)
- 使用枚举类实现单例是最简洁和安全的方式。
- 枚举类天然防止序列化和反射攻击。
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("Doing something...");
}
}
示例代码
以日志管理器为例,实现单例模式:
public class Logger {
// 双重检查锁实现单例
private static volatile Logger instance;
private Logger() {
// 私有构造方法
}
public static Logger getInstance() {
if (instance == null) {
synchronized (Logger.class) {
if (instance == null) {
instance = new Logger();
}
}
}
return instance;
}
public void log(String message) {
System.out.println("Log: " + message);
}
}
// 客户端代码
public class SingletonExample {
public static void main(String[] args) {
Logger logger = Logger.getInstance();
logger.log("Singleton pattern in action!"); // Output: Log: Singleton pattern in action!
}
}
适用场景
- 需要唯一实例:
- 数据库连接池。
- 配置文件管理器。
- 日志管理器。
- 需要全局共享资源:例如系统级别的缓存或计数器。
小结
- 单例模式提供了一个全局的访问点,保证类的唯一实例。
- 在实际开发中,推荐使用 枚举实现 或 静态内部类实现,既简单又高效,能够很好地应对多线程问题和序列化攻击。