1. 单例模式的核心概念
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这个模式在需要控制资源访问或限制实例数量的场景中特别有用。
单例模式的核心在于:
- 私有化构造函数,防止外部直接实例化
- 提供一个静态方法获取唯一实例
- 确保线程安全(在多线程环境下)
java复制public class Singleton {
private static Singleton instance;
private Singleton() {} // 私有构造函数
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
2. 单例模式的实现方式
2.1 饿汉式单例
饿汉式在类加载时就创建实例,保证了线程安全,但可能造成资源浪费。
java复制public class EagerSingleton {
private static final EagerSingleton instance = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return instance;
}
}
2.2 懒汉式单例(非线程安全)
懒汉式在第一次调用时才创建实例,但基础实现不是线程安全的。
java复制public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
2.3 线程安全的懒汉式
通过同步方法确保线程安全,但会影响性能。
java复制public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton() {}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
2.4 双重检查锁定
更高效的线程安全实现,减少同步开销。
java复制public class DoubleCheckedSingleton {
private volatile static DoubleCheckedSingleton instance;
private DoubleCheckedSingleton() {}
public static DoubleCheckedSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckedSingleton.class) {
if (instance == null) {
instance = new DoubleCheckedSingleton();
}
}
}
return instance;
}
}
2.5 静态内部类实现
利用类加载机制保证线程安全,同时实现懒加载。
java复制public class InnerClassSingleton {
private InnerClassSingleton() {}
private static class SingletonHolder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
3. 单例模式的应用场景
3.1 配置管理类
应用程序的配置通常只需要一个实例,单例模式非常适合这种场景。
java复制public class AppConfig {
private static AppConfig instance;
private Properties config;
private AppConfig() {
// 加载配置文件
config = new Properties();
try {
config.load(new FileInputStream("config.properties"));
} catch (IOException e) {
// 处理异常
}
}
public static synchronized AppConfig getInstance() {
if (instance == null) {
instance = new AppConfig();
}
return instance;
}
public String getProperty(String key) {
return config.getProperty(key);
}
}
3.2 数据库连接池
数据库连接池通常只需要一个实例来管理所有连接。
java复制public class ConnectionPool {
private static ConnectionPool instance;
private List<Connection> connections;
private ConnectionPool() {
// 初始化连接池
connections = new ArrayList<>();
for (int i = 0; i < 10; i++) {
connections.add(createConnection());
}
}
public static synchronized ConnectionPool getInstance() {
if (instance == null) {
instance = new ConnectionPool();
}
return instance;
}
public Connection getConnection() {
// 获取可用连接逻辑
}
public void releaseConnection(Connection conn) {
// 释放连接逻辑
}
private Connection createConnection() {
// 创建新连接
}
}
3.3 日志记录器
日志系统通常只需要一个全局实例。
java复制public class Logger {
private static Logger instance;
private PrintWriter writer;
private Logger() {
try {
writer = new PrintWriter(new FileWriter("app.log", true), true);
} catch (IOException e) {
// 处理异常
}
}
public static synchronized Logger getInstance() {
if (instance == null) {
instance = new Logger();
}
return instance;
}
public void log(String message) {
writer.println(new Date() + ": " + message);
}
}
4. 单例模式的注意事项与最佳实践
4.1 序列化问题
单例类如果实现了Serializable接口,反序列化时会创建新实例。解决方法:
java复制public class SerializableSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static SerializableSingleton instance = new SerializableSingleton();
private SerializableSingleton() {}
public static SerializableSingleton getInstance() {
return instance;
}
protected Object readResolve() {
return getInstance();
}
}
4.2 反射攻击防护
通过反射可以绕过私有构造函数,需要额外防护:
java复制public class ReflectionSafeSingleton {
private static ReflectionSafeSingleton instance;
private ReflectionSafeSingleton() {
if (instance != null) {
throw new IllegalStateException("Singleton already initialized");
}
}
public static synchronized ReflectionSafeSingleton getInstance() {
if (instance == null) {
instance = new ReflectionSafeSingleton();
}
return instance;
}
}
4.3 多类加载器环境
在多个类加载器的环境中,单例可能会被多次实例化。解决方法:
java复制public class ClassLoaderSafeSingleton {
private static ClassLoaderSafeSingleton instance;
private ClassLoaderSafeSingleton() {}
public static ClassLoaderSafeSingleton getInstance() {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
synchronized (ClassLoaderSafeSingleton.class) {
if (instance == null) {
ClassLoaderSafeSingleton temp = null;
try {
Class<?> cls = Class.forName(
ClassLoaderSafeSingleton.class.getName(), true, cl);
Method method = cls.getDeclaredMethod("createInstance");
temp = (ClassLoaderSafeSingleton) method.invoke(null);
} catch (Exception e) {
// 处理异常
}
instance = temp;
}
return instance;
}
}
private static ClassLoaderSafeSingleton createInstance() {
return new ClassLoaderSafeSingleton();
}
}
4.4 枚举单例(最佳实践)
Java枚举天然支持单例模式,是最简洁安全的实现方式。
java复制public enum EnumSingleton {
INSTANCE;
public void doSomething() {
// 业务方法
}
}
枚举单例的优势:
- 自动处理序列化问题
- 防止反射攻击
- 线程安全
- 代码简洁
5. 单例模式的替代方案
5.1 依赖注入
在现代应用中,依赖注入框架(如Spring)可以管理单例生命周期,而不需要手动实现单例模式。
java复制@Service // Spring会自动将其作为单例管理
public class UserService {
// 业务逻辑
}
5.2 静态工具类
对于无状态的工具方法,可以使用静态工具类代替单例。
java复制public final class MathUtils {
private MathUtils() {} // 防止实例化
public static int add(int a, int b) {
return a + b;
}
}
5.3 对象池模式
当需要管理多个可重用对象时,对象池模式比单例更合适。
java复制public class ObjectPool<T> {
private List<T> available = new ArrayList<>();
private List<T> inUse = new ArrayList<>();
public T acquire() {
if (available.isEmpty()) {
// 创建新对象
}
T obj = available.remove(0);
inUse.add(obj);
return obj;
}
public void release(T obj) {
inUse.remove(obj);
available.add(obj);
}
}
6. 单例模式的性能考量
6.1 同步开销
不同实现方式的性能差异:
| 实现方式 | 首次访问性能 | 后续访问性能 | 线程安全 |
|---|---|---|---|
| 饿汉式 | 快 | 快 | 是 |
| 懒汉式(同步方法) | 慢 | 慢 | 是 |
| 双重检查 | 中等 | 快 | 是 |
| 静态内部类 | 快 | 快 | 是 |
| 枚举 | 快 | 快 | 是 |
6.2 内存占用
单例对象会一直存在于内存中,需要考虑:
- 单例对象的大小
- 生命周期是否合理
- 是否有内存泄漏风险
6.3 测试考虑
单例模式可能使单元测试变得困难,因为:
- 测试之间可能共享状态
- 难以模拟或替换单例实例
解决方法:
- 使用依赖注入
- 提供重置方法(仅用于测试)
- 使用接口和模拟对象
7. 单例模式在不同语言中的实现
7.1 Python实现
python复制class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
7.2 JavaScript实现
javascript复制const Singleton = (function() {
let instance;
function createInstance() {
const object = new Object("I am the instance");
return object;
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
7.3 C++实现
cpp复制class Singleton {
private:
static Singleton* instance;
Singleton() {} // 私有构造函数
public:
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
// 删除拷贝构造函数和赋值运算符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
// 初始化静态成员
Singleton* Singleton::instance = nullptr;
7.4 Go实现
go复制package singleton
import "sync"
type singleton struct {
// 单例字段
}
var (
instance *singleton
once sync.Once
)
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}
8. 单例模式的常见误用与陷阱
8.1 过度使用单例
单例模式容易被滥用,导致:
- 全局状态难以追踪
- 代码耦合度高
- 测试困难
8.2 单例与多实例需求冲突
当需求变化需要多个实例时,单例实现可能需要重构。
8.3 单例的生命周期管理
单例通常存在于整个应用生命周期,可能导致:
- 资源无法及时释放
- 内存泄漏
- 状态污染
8.4 单例与分布式系统
在分布式系统中,单例模式需要特殊处理:
- 每个JVM/进程有自己的单例实例
- 需要分布式锁或中央存储实现真正的全局单例
9. 单例模式的高级应用
9.1 单例注册表
管理多个单例类的中心化注册表。
java复制public class SingletonRegistry {
private static Map<String, Object> registry = new HashMap<>();
private SingletonRegistry() {}
public static synchronized Object getInstance(String className) {
Object instance = registry.get(className);
if (instance == null) {
try {
Class<?> clazz = Class.forName(className);
instance = clazz.getDeclaredConstructor().newInstance();
registry.put(className, instance);
} catch (Exception e) {
// 处理异常
}
}
return instance;
}
}
9.2 参数化单例
单例实例可以接受初始化参数。
java复制public class ParametrizedSingleton {
private static ParametrizedSingleton instance;
private final String config;
private ParametrizedSingleton(String config) {
this.config = config;
}
public static synchronized void initialize(String config) {
if (instance != null) {
throw new IllegalStateException("Already initialized");
}
instance = new ParametrizedSingleton(config);
}
public static ParametrizedSingleton getInstance() {
if (instance == null) {
throw new IllegalStateException("Not initialized");
}
return instance;
}
}
9.3 单例与多线程池
管理线程池的单例实现。
java复制public class ThreadPoolManager {
private static ThreadPoolManager instance;
private ExecutorService executor;
private ThreadPoolManager() {
executor = Executors.newFixedThreadPool(
Runtime.getRuntime().availableProcessors()
);
}
public static synchronized ThreadPoolManager getInstance() {
if (instance == null) {
instance = new ThreadPoolManager();
}
return instance;
}
public void execute(Runnable task) {
executor.execute(task);
}
public void shutdown() {
executor.shutdown();
}
}
10. 单例模式的设计权衡
10.1 优点
- 严格控制实例数量
- 全局访问点方便使用
- 节省系统资源
- 避免重复创建对象
10.2 缺点
- 违反单一职责原则(管理生命周期+业务逻辑)
- 可能隐藏类之间的依赖关系
- 难以进行单元测试
- 可能产生全局状态问题
10.3 适用场景
- 需要严格控制的共享资源(如配置、连接池)
- 频繁使用的轻量级对象
- 需要唯一实例的场景(如序列号生成器)
10.4 不适用场景
- 需要多态行为的对象
- 需要频繁创建销毁的对象
- 需要测试的场景
- 可能扩展为多实例的场景
