第2课设计模式_单例模式
热度🔥:103 免费课程
授课语音
单例模式(Singleton Pattern)
1. 介绍
单例模式是一种设计模式,其主要目的是确保一个类只有一个实例,并提供一个全局访问点来访问该实例。
单例模式的核心概念
- 唯一性:确保一个类只有一个实例。即使多个线程尝试创建该类的对象,也只会存在一个实例。
- 全局访问:单例模式提供一个全局访问点来获取这个唯一实例,通常通过静态方法实现,以便在任何地方都可以访问该实例。
- 延迟初始化:在一些实现中,单例实例在第一次被请求时才被创建,这样可以避免不必要的开销。这一过程称为延迟初始化(lazy initialization)。
单例模式的变体
- 懒汉式单例(Lazy Initialization):在第一次被访问时创建实例,优点是延迟初始化,但需要处理多线程环境下的同步问题。
- 饿汉式单例(Eager Initialization):在类加载时就创建实例,这种方式简单且线程安全,但如果实例创建的开销较大而应用从未使用该实例,则可能会造成资源浪费。
- 双重检查锁(Double-Check Locking):在多线程环境下,通过双重检查和锁机制来确保实例创建的线程安全性。通常结合
volatile
关键字使用以避免指令重排序带来的问题。 - 登记式单例(Registry-based Singleton):使用注册表管理单例实例。通过静态方法或其他机制从注册表中获取单例实例,适合需要支持动态实例管理的场景。
- 枚举单例(Enum Singleton):使用枚举类型实现单例模式,这是 Java 推荐的实现方式,能够确保线程安全,并防止反序列化攻击。
实际框架中的应用
单例模式在许多实际框架和系统中有广泛的应用。以下是一些常见的应用场景:
配置管理:在Java框架中,配置管理类通常使用单例模式。例如,Spring框架的
ApplicationContext
是一个单例,确保配置在整个应用程序中是一致的。数据库连接池:数据库连接池通常使用单例模式来管理连接。例如,HikariCP或C3P0等数据库连接池在其实现中会使用单例模式,以确保高效管理数据库连接。
日志记录:Java中的日志记录器通常使用单例模式来确保日志记录的一致性。例如,Log4j或SLF4J可以配置为单例,以便在整个应用程序中共享同一个日志记录器实例。
缓存系统:缓存系统如Ehcache或Guava Cache通常使用单例模式,以确保在整个应用程序中只有一个缓存实例,从而提高性能和资源利用率。
Spring应用:在Spring框架中,Bean的默认作用域是单例,确保在整个应用程序上下文中只存在一个实例,以保证共享的服务和配置的一致性。
这些场景展示了单例模式在Java平台上的广泛应用,能够有效管理资源和提供一致性。
2. 代码案例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// 单例模式示例类
public class SingletonExamples {
// 1. 饿汉式单例(Eager Initialization)
public static class EagerSingleton {
// 在类加载时就创建实例,确保线程安全
private static final EagerSingleton INSTANCE = new EagerSingleton();
private String value;
// 私有构造方法,防止外部实例化
private EagerSingleton() {
value = "EagerSingleton Value";
}
// 提供全局访问点
public static EagerSingleton getInstance() {
return INSTANCE;
}
// 显示实例的值
public void showValue() {
System.out.println("EagerSingleton value: " + value);
}
}
// 2. 懒汉式单例(Lazy Initialization)
public static class LazySingleton {
private static LazySingleton instance; // 存储单例实例
private static final Lock lock = new ReentrantLock(); // 锁对象
private String value;
// 私有构造方法
private LazySingleton() {
value = "LazySingleton Value";
}
// 提供全局访问点,使用懒加载方式
public static LazySingleton getInstance() {
if (instance == null) { // 第一次检查
lock.lock(); // 加锁确保线程安全
try {
if (instance == null) { // 第二次检查
instance = new LazySingleton();
}
} finally {
lock.unlock(); // 解锁
}
}
return instance;
}
// 显示实例的值
public void showValue() {
System.out.println("LazySingleton value: " + value);
}
}
// 3. 双重检查锁(Double-Check Locking)
public static class DoubleCheckLockingSingleton {
private static DoubleCheckLockingSingleton instance; // 存储单例实例
private static final Lock lock = new ReentrantLock(); // 锁对象
private String value;
// 私有构造方法
private DoubleCheckLockingSingleton() {
value = "DoubleCheckLockingSingleton Value";
}
// 提供全局访问点,使用双重检查锁机制
public static DoubleCheckLockingSingleton getInstance() {
if (instance == null) { // 第一次检查
lock.lock(); // 加锁
try {
if (instance == null) { // 第二次检查
instance = new DoubleCheckLockingSingleton();
}
} finally {
lock.unlock(); // 解锁
}
}
return instance;
}
// 显示实例的值
public void showValue() {
System.out.println("DoubleCheckLockingSingleton value: " + value);
}
}
// 4. 登记式单例(Registry-based Singleton)
public static class RegistrySingleton {
private static final java.util.Map<String, RegistrySingleton> registry = new java.util.HashMap<>(); // 注册表
private String value;
// 私有构造方法
private RegistrySingleton() {
value = "RegistrySingleton Value";
}
// 提供全局访问点,通过键从注册表获取实例
public static RegistrySingleton getInstance(String key) {
return registry.computeIfAbsent(key, k -> new RegistrySingleton()); // 动态管理单例
}
// 显示实例的值
public void showValue() {
System.out.println("RegistrySingleton value: " + value);
}
}
// 5. 枚举单例(Enum Singleton)
public enum EnumSingleton {
INSTANCE; // 定义唯一的枚举实例
private String value;
// 枚举构造方法
EnumSingleton() {
value = "EnumSingleton Value";
}
// 显示实例的值
public void showValue() {
System.out.println("EnumSingleton value: " + value);
}
}
// 示例代码
public static void main(String[] args) {
// 1. 饿汉式单例
EagerSingleton eager1 = EagerSingleton.getInstance();
EagerSingleton eager2 = EagerSingleton.getInstance();
eager1.showValue();
System.out.println("Are both EagerSingleton instances the same? " + (eager1 == eager2));
// 2. 懒汉式单例
LazySingleton lazy1 = LazySingleton.getInstance();
LazySingleton lazy2 = LazySingleton.getInstance();
lazy1.showValue();
System.out.println("Are both LazySingleton instances the same? " + (lazy1 == lazy2));
// 3. 双重检查锁
DoubleCheckLockingSingleton doubleCheck1 = DoubleCheckLockingSingleton.getInstance();
DoubleCheckLockingSingleton doubleCheck2 = DoubleCheckLockingSingleton.getInstance();
doubleCheck1.showValue();
System.out.println("Are both DoubleCheckLockingSingleton instances the same? " + (doubleCheck1 == doubleCheck2));
// 4. 登记式单例
RegistrySingleton registry1 = RegistrySingleton.getInstance("key1");
RegistrySingleton registry2 = RegistrySingleton.getInstance("key1");
registry1.showValue();
System.out.println("Are both RegistrySingleton instances the same? " + (registry1 == registry2));
// 5. 枚举单例
EnumSingleton enumSingleton = EnumSingleton.INSTANCE;
enumSingleton.showValue();
}
}
详细中文注释
- 类说明:每个单例实现类都有一个内部初始化方法,用于设置初始值。在每个
showValue
方法中打印该实例的值,以验证实例的唯一性。 - 示例代码:在主程序中,我们创建每种单例的两个实例并检查它们是否相同,以证明单例模式的有效性。每个实例调用
showValue
方法显示其值。