单例模式(Singleton Pattern)
单例模式(Singleton Pattern)
单例模式是一种创建型设计模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点;单例模式通常用于控制资源的共享访问,例如数据库连接、线程池、日志对象等。
代码示例
1. 懒汉式单例(Lazy Initialization)
懒汉式单例在第一次调用时创建实例,适合资源敏感的场景。
/**
* 懒汉式单例:线程不安全
*
* @author Viices Cai
* @time 2020/7/5
*/
public class LazySingleton {
// 私有静态变量,用于保存唯一实例
private static LazySingleton instance;
// 私有构造方法,防止外部直接创建实例
private LazySingleton() { }
// 公共静态方法,提供全局访问点
public static LazySingleton getInstance() {
if (instance == null) {
// 第一次调用时创建实例
instance = new LazySingleton();
}
return instance;
}
}
- 特点:延迟初始化,适合资源敏感的场景,但需要处理线程安全问题。
2.饿汉式单例(Eager Initialization)
饿汉式单例在类加载时创建实例,适合资源不敏感的场景。
/**
* 饿汉式单例
*
* @author Viices Cai
* @time 2020/7/5
*/
public class EagerSingleton {
// 私有静态变量,类加载时创建实例
private static final EagerSingleton instance = new EagerSingleton();
// 私有构造方法,防止外部直接创建实例
private EagerSingleton(){ }
// 公共静态方法,提供全局访问点
public static EagerSingleton getInstance() {
return instance;
}
}
- 类加载时初始化,适合资源不敏感的场景,线程安全。
3.静态块式单例(Static-Block Singleton)
饿汉式单例的另一种实现方式。
/**
* 静态块式单例:同饿汉式
*
* @author Viices Cai
* @time 2022/1/13
*/
public class StaticBlockSingleton {
// 私有静态变量,用于保存唯一实例
private static StaticBlockSingleton instance;
// 私有构造方法,防止外部直接创建实例
private StaticBlockSingleton() { }
static { // 静态块,创建实例
try {
instance = new StaticBlockSingleton();
} catch (Exception e) {
throw new RuntimeException("Exception occured in creating singleton instance.");
}
}
// 公共静态方法,提供全局访问点
public static StaticBlockSingleton getInstance() {
return instance;
}
}
4. 同步锁式单例(Synchronized-Locked Singleton)
在懒汉式的基础上解决了其线程不安全的问题,但是加锁也带来了额外的开销。
/**
* 同步锁模式
*
* @author Viices Cai
* @time 2020/7/5
*/
public class LockSingleton {
// 私有静态变量,用于保存唯一实例
private static LockSingleton instance;
// 私有构造方法,防止外部直接创建实例
private LockSingleton() {}
// 在原有的访问方法基础上加锁,避免线程抢占
public synchronized static LockSingleton getInstance() {
if (instance == null) {
instance = new LockSingleton();
}
return instance;
}
}
5.双重锁定式单例(Double-Checked Locking Singleton)
双重检查模式,在同步锁的实现基础上进行了改进。两次判断:第一次是为了避免重复创建实例;第二次是为了进行同步,避免多线程问题,使得其线程安全,且在多线程下能保持高性能。
/**
* 双重锁定式单例
*
* @author Viices Cai
* @time 2020/7/5
*/
public class DoubleCheckLockingSingleton {
// 使用 volatile 关键字确保多线程环境下的可见性
private volatile static DoubleCheckLockingSingleton instance;
// 私有构造方法,防止外部直接创建实例
private DoubleCheckLockingSingleton() { }
// 公共静态方法,提供全局访问点
public static DoubleCheckLockingSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckLockingSingleton.class) { // 加锁
if (instance == null) { // 双重检查
instance = new DoubleCheckLockingSingleton();
}
}
}
return instance;
}
}
- 创建过程解析:
- 分配内存。
- .初始化对象:调用构造器。
- 指向刚分配的地址。
- 若发生了指令重排序:即由于
CPU
调度问题执行顺序可能无法按照我们理想的情况进行,如:线程A执行了1、3此时未执行2,但是线程B进入了发现存在内存地址,但实际上对象并不存在。 - 必须使用
volatile
避免重排序。- 禁止进行指令重排序。
- 若发生了指令重排序:即由于
6.枚举单例(Enum Singleton)
推荐使用,天然支持线程安全和防止反射攻击。
/**
* 枚举式单例
*
* @author Viices Cai
* @time 2020/7/5
*/
public enum EnumSingleton {
INSTANCE; // 单例实例
// 示例方法
public void showMessage() {
System.out.println("Hello, World!");
}
}
应用场景
-
资源共享
当需要全局共享一个资源(如数据库连接、线程池、配置文件等)时,可以使用单例模式。
-
控制实例数量
当需要严格控制一个类的实例数量(只能有一个实例)时,可以使用单例模式。
-
延迟初始化
当需要在第一次使用时才创建实例时,可以使用懒汉式单例。
在 Java 中的应用
- Runtime 类:Java 的
Runtime
类是一个典型的单例,用于管理应用程序的运行环境。 - Spring 框架:Spring 中的 Bean 默认是单例的,通过容器管理单例对象的生命周期。
优缺点
-
优点:
-
全局唯一实例
确保一个类只有一个实例,避免资源冲突。
-
延迟初始化
懒汉式单例可以在第一次使用时才创建实例,节省资源。
-
全局访问点
通过静态方法提供全局访问点,方便调用。
-
-
缺点:
-
难以扩展
单例模式通常难以扩展,因为它的构造方法是私有的。
-
隐藏依赖
单例模式可能会掩盖不良设计, 比如程序各组件之间耦合过多。
-
线程安全问题
懒汉式单例在多线程环境下需要额外处理线程安全问题。
-
总结
单例模式是一种简单但强大的设计模式,适用于需要全局唯一实例的场景。它通过私有化构造方法和提供全局访问点,确保一个类只有一个实例。单例模式在资源共享、延迟初始化和控制实例数量等场景中非常有用。然而,单例模式也存在扩展性差、隐藏依赖和线程安全问题等缺点,需要根据具体场景谨慎使用。在Java中,单例模式广泛应用于 Runtime
类和 Spring 框架中。