单例模式 (Singleton) 全面讲解
学习笔记
设计模式
单例模式
Java
线程安全
编程实践
本文详细介绍了单例模式的概念、实现方式及其在多线程环境下的安全性问题,包括懒汉式和线程安全版的实现代码。
单例模式 (Singleton) 全面讲解
1. 什么是单例模式?
单例模式确保一个类只有一个实例,并提供一个全局访问点。
- 为什么需要?
比如数据库连接池、日志管理器、配置加载等场景,如果重复创建实例会浪费资源或导致数据不一致。
2. 单例模式的核心实现
关键点:
- 私有构造函数:防止外部通过
new创建实例。 - 静态私有变量:保存唯一实例。
- 静态公有方法:提供全局访问点(如
getInstance())。
2.1 基础版(懒汉式,线程不安全)
public class Singleton {
private static Singleton instance; // 静态私有变量
private Singleton() {} // 私有构造函数
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // 延迟初始化
}
return instance;
}
}
- 问题:多线程环境下可能创建多个实例(考试常考!)。
2.2 线程安全版(加锁)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() { // 加锁
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
- 缺点:每次调用
getInstance()都加锁,性能差。
2.3 双重检查锁(DCL,最优解)
public class Singleton {
private static volatile Singleton instance; // volatile防止指令重排序
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) { // 加锁
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
- 为什么用
volatile?
防止JVM指令重排序导致未初始化的对象被返回(进阶考点)。
2.4 饿汉式(线程安全,推荐)
public class Singleton {
private static final Singleton instance = new Singleton(); // 类加载时初始化
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
- 优点:简单、线程安全(利用类加载机制)。
- 缺点:即使不用也会创建实例,可能浪费内存。
3. 应用场景
- 经典用途:
- 数据库连接池(如
DataSource)。 - 日志管理器(全局共享日志文件)。
- 配置信息类(如
AppConfig)。
- 数据库连接池(如
- 面试考点:
- 如何破坏单例?(反射、序列化)
- 如何防御?(枚举单例、
readResolve()方法)
4. 互动实践
问题1:以下代码是单例吗?为什么?
public class FakeSingleton {
public static FakeSingleton instance = new FakeSingleton();
private FakeSingleton() {}
}
- 答案:不是!因为
instance是public的,外部可直接修改,如FakeSingleton.instance = null;。
问题2:如何用枚举实现单例?
public enum EnumSingleton { // 枚举天然防反射、序列化破坏
INSTANCE;
public void doSomething() {
System.out.println("Singleton method");
}
}
- 调用方式:
EnumSingleton.INSTANCE.doSomething(); - 优点:最简单、最安全的单例实现(Joshua Bloch推荐)。
5. 考试练习题
题目1(基础)
单例模式的目的是什么?
A. 限制类的实例化次数
B. 提高类的性能
C. 允许子类覆盖父类方法
D. 实现多态
答案:A
题目2(代码填空)
补全以下双重检查锁单例模式的代码:
public class Singleton {
private static ____ Singleton instance; // 关键字填空
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
____ (Singleton.class) {
if (instance == null) {
instance = ____ Singleton();
}
}
}
return instance;
}
}
答案:
volatilesynchronizednew
题目3(进阶)
为什么双重检查锁需要volatile关键字?
答案:防止JVM指令重排序导致其他线程获取到未初始化的对象。