单例模式 (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");注意事项
反射攻击:可通过反射调用私有构造方法
- 解决方案:在构造方法中添加判断
序列化问题:反序列化会创建新实例
- 解决方案:实现
readResolve()方法
- 解决方案:实现
线程安全:多线程环境下要注意同步问题
优缺点
| 优点 | 缺点 |
|---|---|
| 节省资源 | 违反单一职责原则 |
| 全局访问 | 测试困难 |
| 控制实例数 | 隐藏依赖关系 |
总结
单例模式在 Java 开发中非常常见,推荐使用枚举或静态内部类方式实现。在 Spring 框架中,默认就是以单例模式管理 Bean,通常不需要自己实现。