目录

单例模式 (Singleton Pattern)

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

概述

单例模式是最简单的设计模式之一,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。

类图结构

适用场景

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

实现方式

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. 懒汉式(线程安全 - 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 {
    // 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;
    }
}

优点:延迟加载,线程安全,性能高

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() {
        System.out.println("Doing something...");
    }
}

// 使用
Singleton.INSTANCE.doSomething();

优点:线程安全,防止反射攻击,防止序列化问题

框架中的应用

Spring 中的单例

@Service
public class UserService {
    // Spring 默认以单例模式管理 Bean
}

// 配置作用域
@Scope("singleton")
@Component
public class CacheManager {
    // 显式声明为单例
}

JDK 中的单例

// Runtime 类
Runtime runtime = Runtime.getRuntime();

// Desktop 类
Desktop desktop = Desktop.getDesktop();

数据库连接池示例

public class ConnectionPool {
    private static volatile ConnectionPool instance;
    private List<Connection> connections;
    private static final int MAX_CONNECTIONS = 10;
    
    private ConnectionPool() {
        connections = new ArrayList<>();
        for (int i = 0; i < MAX_CONNECTIONS; i++) {
            connections.add(createConnection());
        }
    }
    
    public static ConnectionPool getInstance() {
        if (instance == null) {
            synchronized (ConnectionPool.class) {
                if (instance == null) {
                    instance = new ConnectionPool();
                }
            }
        }
        return instance;
    }
    
    public Connection getConnection() {
        // 从池中获取连接
        return connections.remove(0);
    }
    
    public void releaseConnection(Connection conn) {
        // 释放连接回池中
        connections.add(conn);
    }
    
    private Connection createConnection() {
        // 创建数据库连接
        return null;
    }
}

配置管理器示例

public class ConfigManager {
    private static final ConfigManager INSTANCE = new ConfigManager();
    private Properties config;
    
    private ConfigManager() {
        config = new Properties();
        loadConfig();
    }
    
    public static ConfigManager getInstance() {
        return INSTANCE;
    }
    
    private void loadConfig() {
        try (InputStream is = getClass()
                .getResourceAsStream("/config.properties")) {
            config.load(is);
        } catch (IOException e) {
            throw new RuntimeException("加载配置失败", e);
        }
    }
    
    public String getProperty(String key) {
        return config.getProperty(key);
    }
}

// 使用
String dbUrl = ConfigManager.getInstance()
    .getProperty("database.url");

注意事项

  1. 反射攻击:可通过反射调用私有构造方法

    • 解决方案:在构造方法中添加判断
  2. 序列化问题:反序列化会创建新实例

    • 解决方案:实现 readResolve() 方法
  3. 线程安全:多线程环境下要注意同步问题

优缺点

优点缺点
节省资源违反单一职责原则
全局访问测试困难
控制实例数隐藏依赖关系

总结

单例模式在 Java 开发中非常常见,推荐使用枚举静态内部类方式实现。在 Spring 框架中,默认就是以单例模式管理 Bean,通常不需要自己实现。