题目
单例模式在Java中的实现与应用
信息
- 类型:问答
- 难度:⭐
考点
单例模式概念,线程安全实现,应用场景
快速回答
单例模式确保一个类只有一个实例,并提供全局访问点。线程安全实现要点:
- 私有化构造方法
- 静态私有成员变量保存实例
- 静态工厂方法获取实例
- 使用双重检查锁定(Double-Checked Locking)保证线程安全
示例代码:
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;
}
}
## 解析
1. 单例模式原理
单例模式(Singleton Pattern)属于创建型设计模式,核心思想是:
- 确保类只有一个实例存在
- 提供全局访问点(通常为静态方法)
- 自行创建实例(私有构造器)
设计意图:当系统需要全局唯一对象时(如配置管理器、线程池、数据库连接池),避免重复创建消耗资源。
2. 线程安全实现(双重检查锁定)
完整代码示例:
public class DatabaseConnection {
// volatile保证可见性和禁止指令重排序
private static volatile DatabaseConnection instance;
// 私有构造器阻止外部实例化
private DatabaseConnection() {
// 初始化数据库连接
}
public static DatabaseConnection getInstance() {
// 第一次检查:避免不必要的同步
if (instance == null) {
synchronized (DatabaseConnection.class) {
// 第二次检查:确保只有一个线程创建实例
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}
// 示例方法
public void executeQuery(String sql) {
System.out.println("Executing: " + sql);
}
}
// 使用示例
public class Main {
public static void main(String[] args) {
DatabaseConnection conn1 = DatabaseConnection.getInstance();
DatabaseConnection conn2 = DatabaseConnection.getInstance();
System.out.println(conn1 == conn2); // 输出 true
conn1.executeQuery("SELECT * FROM users");
}
}3. 关键点说明
- volatile关键字:防止JVM指令重排序,确保其他线程看到完全初始化的对象
- 双重检查:外层判断避免每次调用都同步,内层判断防止多次实例化
- 私有构造器:彻底阻止通过new关键字创建实例
4. 最佳实践
- 枚举实现(推荐):Java枚举天然支持单例,绝对防止反射攻击
public enum EnumSingleton { INSTANCE; public void doSomething() { /*...*/ } } - 静态内部类:利用类加载机制保证线程安全
public class Singleton { private Singleton() {} private static class Holder { static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }
5. 常见错误
| 错误实现 | 问题 | 修复方案 |
|---|---|---|
| 未加锁的懒汉式 | 多线程下可能创建多个实例 | 添加同步控制 |
| 直接在方法上加锁 | 每次访问都同步,性能差 | 改用双重检查 |
| 缺少volatile | 可能返回未完全初始化的对象 | 声明变量为volatile |
6. 应用场景
- 配置信息管理器(全局唯一配置)
- 日志记录器(避免日志文件冲突)
- 数据库连接池(复用连接资源)
- Spring默认Bean作用域(单例Bean)
7. 扩展知识
- 破坏单例的方式:反射、序列化/反序列化
- 防护措施:枚举实现、重写readResolve()方法
- 与静态工具类的区别:单例可以有状态且支持接口继承