薬屋 | iren
839 words
4 minutes
单例模式
1. 基本介绍
单例模式(Singleton Pattern)是一种设计模式,旨在确保一个类只有一个实例,并提供一个全局访问点来获取该实例。它主要解决的问题是如何保证某个类的实例在整个应用程序中唯一,并且能够在需要时轻松访问。
单例模式的关键点:
- 唯一性:类只能有一个实例,确保系统中只会有这个唯一的对象。
- 全局访问:提供一个全局访问点来获取该实例。通常通过一个静态方法来获取实例。
单例模式的应用场景:
- 日志记录器:全局唯一的日志工具类。
- 数据库连接池:多个线程共享一个数据库连接池。
- 线程池:线程池是全局唯一的,避免重复创建。
- 缓存管理:全局唯一的缓存对象。
2. Java实现
1. 饿汉式(Eager Initialization)
在类加载时就创建实例,线程安全,但可能会浪费资源(如果实例未被使用)。
public class Singleton {
// 在类加载时创建实例
private static final Singleton INSTANCE = new Singleton();
// 私有构造函数,防止外部实例化
private Singleton() {}
// 提供全局访问点
public static Singleton getInstance() {
return INSTANCE;
}
}
- 优点:实现简单,线程安全。
- 缺点:如果实例未被使用,会造成资源浪费。
2. 懒汉式(Lazy Initialization)
在第一次调用 getInstance()
时创建实例,节省资源,但需要处理多线程问题。
(1)非线程安全的懒汉式:多线程环境下可能会创建多个实例
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
(2)线程安全的懒汉式(加锁):每次调用 getInstance()
都会加锁,性能较差。
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3. 双重检查锁(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;
}
}
- 优点:线程安全,且只有在第一次创建实例时加锁。
- 缺点:实现稍复杂,需要
volatile
关键字防止指令重排序。
4. 静态内部类(Static Inner Class)
利用类加载机制保证线程安全,同时实现懒加载。
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
- 优点:线程安全,懒加载,实现简单。
- 缺点:无法传递参数初始化实例。
5. 枚举(Enum)
利用枚举的特性实现单例,线程安全且防止反射攻击。
public enum Singleton {
INSTANCE;
public void doSomething() {
System.out.println("Doing something...");
}
}
- 优点:线程安全,防止反射和序列化破坏单例。
- 缺点:不够灵活(如无法延迟加载)。
6. ThreadLocal 单例
为每个线程提供一个单例实例,线程间隔离。
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
ThreadLocal.withInitial(ThreadLocalSingleton::new);
private ThreadLocalSingleton() {}
public static ThreadLocalSingleton getInstance() {
return threadLocalInstance.get();
}
}
- 优点:线程间隔离,适合线程内单例场景。
- 缺点:每个线程都有自己的实例,不是全局单例。