一、单例模式的基本概念
1.1 什么是单例模式
确保一个类只有一个实例,并提供一个全局访问点。
1.2 使用场景
-
配置管理器
-
数据库连接池
-
日志记录器
-
线程池
-
缓存系统
二、非安全单例模式(存在问题)
2.1 懒汉式(线程不安全)
java
public class UnsafeSingleton {
private static UnsafeSingleton instance;
private UnsafeSingleton() {
// 防止反射创建
if (instance != null) {
throw new RuntimeException("Use getInstance() method to get the single instance");
}
}
public static UnsafeSingleton getInstance() {
if (instance == null) { // 多线程可能同时进入
instance = new UnsafeSingleton();
}
return instance;
}
}
问题:多线程环境下可能创建多个实例
三、线程安全的单例实现方式
3.1 饿汉式(Eager Initialization)
java
public class EagerSingleton {
// 类加载时立即初始化(线程安全)
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {
// 防止反射攻击
if (INSTANCE != null) {
throw new RuntimeException("Use getInstance() method to get the single instance");
}
}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
优点 :简单、线程安全
缺点:类加载时就创建,可能造成资源浪费
3.2 静态代码块饿汉式
java
public class StaticBlockSingleton {
private static final StaticBlockSingleton INSTANCE;
static {
try {
INSTANCE = new StaticBlockSingleton();
} catch (Exception e) {
throw new RuntimeException("Error creating singleton instance", e);
}
}
private StaticBlockSingleton() {
// 初始化
}
public static StaticBlockSingleton getInstance() {
return INSTANCE;
}
}
3.3 懒汉式(同步方法)
java
public class SynchronizedSingleton {
private static SynchronizedSingleton instance;
private SynchronizedSingleton() {
if (instance != null) {
throw new RuntimeException("Use getInstance() method to get the single instance");
}
}
// 同步方法,线程安全但性能差
public static synchronized SynchronizedSingleton getInstance() {
if (instance == null) {
instance = new SynchronizedSingleton();
}
return instance;
}
}
缺点:每次获取实例都需要同步,性能差
3.4 双重检查锁(Double-Checked Locking)
java
public class DoubleCheckedSingleton {
// 使用volatile防止指令重排
private static volatile DoubleCheckedSingleton instance;
private DoubleCheckedSingleton() {
if (instance != null) {
throw new RuntimeException("Use getInstance() method to get the single instance");
}
}
public static DoubleCheckedSingleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) { // 第二次检查
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}
注意 :必须使用volatile关键字,防止指令重排序问题
3.5 静态内部类(Bill Pugh Singleton)
java
public class InnerClassSingleton {
private InnerClassSingleton() {
// 防止反射攻击
if (SingletonHolder.INSTANCE != null) {
throw new RuntimeException("Use getInstance() method to get the single instance");
}
}
// 静态内部类在需要时才加载
private static class SingletonHolder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
优点:延迟加载、线程安全、高效
3.6 枚举(Joshua Bloch推荐)
java
public enum EnumSingleton {
INSTANCE;
private int value;
public void doSomething() {
System.out.println("Singleton with enum");
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
// 使用方法
public static void main(String[] args) {
EnumSingleton.INSTANCE.doSomething();
EnumSingleton.INSTANCE.setValue(10);
}
}
优点:
-
防止反射攻击(枚举不能通过反射创建)
-
防止反序列化创建新对象
-
线程安全
-
代码简洁
四、防止破坏单例的方法
4.1 防止反射攻击
java
public class AntiReflectionSingleton {
private static volatile AntiReflectionSingleton instance;
private static boolean initialized = false;
private AntiReflectionSingleton() {
synchronized (AntiReflectionSingleton.class) {
if (initialized) {
throw new RuntimeException("Cannot reflectively create singleton objects");
}
initialized = true;
}
}
public static AntiReflectionSingleton getInstance() {
if (instance == null) {
synchronized (AntiReflectionSingleton.class) {
if (instance == null) {
instance = new AntiReflectionSingleton();
}
}
}
return instance;
}
}
4.2 防止序列化破坏
java
import java.io.Serializable;
public class SerializableSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static final SerializableSingleton INSTANCE = new SerializableSingleton();
private SerializableSingleton() {
if (INSTANCE != null) {
throw new RuntimeException("Use getInstance() method to get the single instance");
}
}
public static SerializableSingleton getInstance() {
return INSTANCE;
}
// 关键方法:防止反序列化创建新实例
protected Object readResolve() {
return getInstance();
}
}
4.3 完整的安全单例示例
java
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicReference;
/**
* 线程安全、防止反射、防止序列化破坏的单例模式
*/
public class PerfectSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static final AtomicReference<PerfectSingleton> INSTANCE =
new AtomicReference<>();
private static volatile boolean initialized = false;
private PerfectSingleton() {
synchronized (PerfectSingleton.class) {
if (initialized) {
throw new RuntimeException("Cannot reflectively create singleton objects");
}
initialized = true;
}
// 初始化代码
}
public static PerfectSingleton getInstance() {
PerfectSingleton current = INSTANCE.get();
if (current == null) {
synchronized (PerfectSingleton.class) {
current = INSTANCE.get();
if (current == null) {
current = new PerfectSingleton();
INSTANCE.set(current);
}
}
}
return current;
}
protected Object readResolve() {
return getInstance();
}
// 防止克隆
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Singleton cannot be cloned");
}
}
五、现代Java中的单例实现
5.1 使用Java 8+的Supplier接口
java
import java.util.function.Supplier;
public class SupplierSingleton {
private static final Supplier<SupplierSingleton> INSTANCE =
SupplierSingleton::createInstance;
private SupplierSingleton() {
if (Holder.INSTANCE != null) {
throw new RuntimeException("Use getInstance() method");
}
}
private static SupplierSingleton createInstance() {
return new SupplierSingleton();
}
private static class Holder {
static final SupplierSingleton INSTANCE = SupplierSingleton.getInstance();
}
public static SupplierSingleton getInstance() {
return INSTANCE.get();
}
}
5.2 使用CompletableFuture实现延迟加载
java
import java.util.concurrent.CompletableFuture;
public class AsyncSingleton {
private static CompletableFuture<AsyncSingleton> future;
private AsyncSingleton() {
// 初始化
}
public static CompletableFuture<AsyncSingleton> getInstanceAsync() {
if (future == null) {
synchronized (AsyncSingleton.class) {
if (future == null) {
future = CompletableFuture.supplyAsync(AsyncSingleton::new);
}
}
}
return future;
}
// 同步获取
public static AsyncSingleton getInstance() {
try {
return getInstanceAsync().get();
} catch (Exception e) {
throw new RuntimeException("Error getting singleton instance", e);
}
}
}
六、单例模式的最佳实践
6.1 选择建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 简单应用 | 饿汉式 | 简单可靠 |
| 需要延迟加载 | 静态内部类 | 线程安全,延迟加载 |
| 需要防止反射和序列化攻击 | 枚举 | 最安全 |
| 需要高性能 | 双重检查锁 | 性能好,延迟加载 |
6.2 代码规范示例
java
/**
* 配置管理器单例
* 使用枚举实现,确保线程安全和防止反射攻击
*/
public enum ConfigManager {
INSTANCE;
private Properties config;
private ConfigManager() {
loadConfig();
}
private void loadConfig() {
config = new Properties();
try (InputStream input = getClass().getClassLoader()
.getResourceAsStream("config.properties")) {
config.load(input);
} catch (IOException e) {
throw new RuntimeException("Failed to load configuration", e);
}
}
public String getProperty(String key) {
return config.getProperty(key);
}
public String getProperty(String key, String defaultValue) {
return config.getProperty(key, defaultValue);
}
}
6.3 测试单例模式
java
import java.lang.reflect.Constructor;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingletonTest {
// 测试多线程安全性
public static void testThreadSafety() throws InterruptedException {
final int threadCount = 100;
final CountDownLatch latch = new CountDownLatch(threadCount);
final Set<Object> instances = Collections.synchronizedSet(new HashSet<>());
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
for (int i = 0; i < threadCount; i++) {
executor.execute(() -> {
instances.add(DoubleCheckedSingleton.getInstance());
latch.countDown();
});
}
latch.await();
executor.shutdown();
System.out.println("Unique instances created: " + instances.size());
// 应该是1
}
// 测试反射攻击
public static void testReflectionAttack() {
try {
Constructor<DoubleCheckedSingleton> constructor =
DoubleCheckedSingleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
DoubleCheckedSingleton instance1 = constructor.newInstance();
DoubleCheckedSingleton instance2 = constructor.newInstance();
System.out.println("Reflection attack succeeded: " +
(instance1 != instance2));
} catch (Exception e) {
System.out.println("Reflection attack failed: " + e.getMessage());
}
}
}
七、Spring框架中的单例
7.1 Spring单例 vs Java单例
java
@Component
@Scope("singleton") // 默认就是singleton
public class SpringSingleton {
// Spring容器管理的单例
// 与Java单例的区别:
// 1. 作用域不同(应用上下文)
// 2. 生命周期由Spring管理
// 3. 默认是单例,但可以配置
}
7.2 线程安全的Spring Bean
java
@Service
public class UserService {
// Service通常是无状态的,天然线程安全
// 如果需要状态,使用ThreadLocal
private final ThreadLocal<User> currentUser = new ThreadLocal<>();
public void setCurrentUser(User user) {
currentUser.set(user);
}
public User getCurrentUser() {
return currentUser.get();
}
}
八、总结
8.1 安全单例的要点
-
线程安全:确保多线程环境下只创建一个实例
-
延迟加载:避免不必要的资源占用
-
防止反射:通过标志位或异常阻止反射创建
-
防止序列化 :实现
readResolve()方法 -
防止克隆 :重写
clone()方法并抛出异常
8.2 推荐方案
-
简单场景:使用饿汉式
-
常规需求:使用静态内部类
-
高安全要求:使用枚举
-
复杂初始化:使用双重检查锁
8.3 注意事项
-
单例模式可能隐藏类之间的依赖关系
-
单例的单元测试可能比较困难
-
考虑使用依赖注入框架管理单例
-
避免在单例中保存可变状态
选择适合的单例实现方式需要综合考虑线程安全、性能、序列化需求以及具体的使用场景。