单例模式
单例模式(Singleton Pattern)属于创建型模式的一种。它的核心目的是控制类的实例化过程,确保在任何情况下,对于特定类来说,在整个应用程序中最多只有一个实例存在,并提供一个全局访问点来获取这个唯一的实例。这样做的主要好处是能够有效地控制资源的使用,特别是那些需要频繁创建和销毁的对象,比如数据库连接池、线程池、缓存服务等。
单例模式的特点
- 唯一性:确保系统中该类只有一个实例。
- 全局访问点:提供一个全局访问点来获取这个实例,通常通过静态方法实现。
- 延迟初始化:根据实现方式的不同,实例化可以在类加载时完成(饿汉式),或者首次请求实例时完成(懒汉式)。
- 线程安全:在多线程环境下,必须确保单例的创建过程是线程安全的。
实现单例模式的常见方法
提示
- 一个私有构造函数
- 一个私有静态变量
- 一个公有静态函数
1. 饿汉式(静态常量)
在类加载时就完成了初始化,避免了线程同步问题,但可能会造成资源浪费。
java
public class Singleton {
private final static Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return INSTANCE;
}
}
2. 懒汉式(线程不安全)
首次使用时才创建实例,但原始实现线程不安全。
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
3. 懒汉式(线程安全,同步方法)
通过在getInstance方法上加锁保证线程安全,但每次访问都会同步,影响性能。
java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public synchronized static Singleton getInstance() {
if(instance == null) {
instance = new Singleton();
}
return instance;
}
}
4. 双重检查锁定(DCL, Double-Checked Locking)
结合懒汉式和性能优化,既实现了延迟加载,又减少了锁的开销。
java
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;
}
}
5. 静态内部类
利用Java类加载机制保证线程安全,且实现懒加载。
java
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
6.枚举
使用枚举来实现单例是一种既简单又线程安全的方法,同时还能防止反序列化破坏单例的特性。
java
public enum Singleton {
INSTANCE;
// 可以在这里添加其他实例方法或属性
public void someBusinessMethod() {
// 实际业务逻辑
}
}
使用枚举实现单例的优点:
- 线程安全:Java保证了枚举实例的创建是线程安全的,因此无需担心多线程环境下的实例化问题。
- 简洁:实现代码非常简洁,易于理解和维护。
- 防止反射攻击:由于枚举类型的特殊性,它能天然防止通过反射来实例化多个对象。
- 防止序列化破坏:如果实现
Serializable
接口,枚举的序列化和反序列化机制会保证单例特性不会被破坏。
如何使用:
访问单例实例就像访问枚举值一样简单:
java
Singleton singleton = Singleton.INSTANCE;
singleton.someBusinessMethod();
虽然枚举实现单例的方式可能不是最直观的,但它提供了高度的线程安全性和简洁性,特别是在对并发控制有严格要求的场景下,是一个值得推荐的实现方式。
应用场景
- 当需要控制资源的共享,例如数据库连接池。
- 当对象需要被全局访问,如日志记录器、配置管理器。
- 当对象的创建成本较高,如复杂的初始化过程,需要保证只创建一次。
注意事项
- 单例模式可能引入全局状态,增加模块间的耦合,需谨慎使用。
- 反射和序列化可能破坏单例,需要额外处理来防御。
- 在并发编程中,确保单例的线程安全性是非常重要的。