单例模式
单例模式
模式简介
单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。单例模式通过私有化构造方法、提供静态方法或静态变量来实现唯一实例的创建和访问。
单例模式适用于需要确保全局只有一个实例的场景,如配置管理、日志记录、数据库连接池等。
懒汉型 vs 饿汉型单例模式
单例模式的核心实现方式主要分为两大类:
饿汉型(Eager Initialization)
核心思想:类加载时就立即创建实例,无论是否会被使用。
特点:
- 实现简单,线程安全(JVM类加载机制保证)
- 类加载时就创建实例,可能造成资源浪费
- 没有延迟加载的特性
适用场景:
- 单例对象体积较小,创建开销低
- 单例对象肯定会被使用
- 对启动性能要求不高的场景
懒汉型(Lazy Initialization)
核心思想:只有在第一次调用getInstance()方法时才创建实例,实现延迟加载。
特点:
- 延迟加载,节省资源
- 实现相对复杂,需要考虑线程安全问题
- 启动速度快,运行时按需创建
常见实现方式:
- 线程不安全版
- 同步方法版
- 双重检查锁定版
适用场景:
- 单例对象体积较大,创建开销高
- 单例对象可能不会被使用
- 对启动性能要求较高的场景
类结构
singleton/
├── Singleton.java
└── SingletonTest.java
各个类的作用
1. EagerSingleton 类
- 饿汉式(立即加载)
- 类加载时就创建实例
- 包含一个静态 final 成员变量
instance - 私有构造方法
- 提供静态方法
getInstance()返回实例 - 提供
showMessage()方法显示信息
2. LazySingletonUnsafe 类
- 懒汉式(延迟加载,线程不安全)
- 包含一个静态成员变量
instance - 私有构造方法
- 提供静态方法
getInstance(),当instance为 null 时创建实例 - 线程不安全,多线程环境下可能创建多个实例
- 提供
showMessage()方法显示信息
3. LazySingletonSafe 类
- 懒汉式(线程安全,同步方法)
- 包含一个静态成员变量
instance - 私有构造方法
- 提供同步静态方法
getInstance() - 线程安全,但效率较低
- 提供
showMessage()方法显示信息
4. LazySingletonDoubleCheck 类
- 懒汉式(线程安全,双重检查锁定)
- 包含一个 volatile 静态成员变量
instance - 私有构造方法
- 提供静态方法
getInstance(),使用双重检查锁定 - 线程安全,且效率较高
- 提供
showMessage()方法显示信息
5. StaticInnerClassSingleton 类
- 静态内部类
- 私有构造方法
- 包含一个静态内部类
SingletonHolder - 静态内部类包含一个静态 final 成员变量
instance - 提供静态方法
getInstance()返回SingletonHolder.instance - 线程安全,且效率较高
- 提供
showMessage()方法显示信息
6. EnumSingleton 枚举
- 枚举
- 包含一个枚举常量
INSTANCE - 提供
showMessage()方法显示信息 - 线程安全,且实现简单
7. SingletonTest 类
- 测试类,演示单例模式的使用
- 测试各种单例实现的功能
使用示例
客户端代码示例
// 测试饿汉式单例
EagerSingleton eager1 = EagerSingleton.getInstance();
EagerSingleton eager2 = EagerSingleton.getInstance();
System.out.println("饿汉式单例是否为同一实例: " + (eager1 == eager2));
eager1.showMessage();
// 测试懒汉式单例(线程不安全)
LazySingletonUnsafe lazyUnsafe1 = LazySingletonUnsafe.getInstance();
LazySingletonUnsafe lazyUnsafe2 = LazySingletonUnsafe.getInstance();
System.out.println("懒汉式单例(线程不安全)是否为同一实例: " + (lazyUnsafe1 == lazyUnsafe2));
lazyUnsafe1.showMessage();
// 测试懒汉式单例(线程安全,同步方法)
LazySingletonSafe lazySafe1 = LazySingletonSafe.getInstance();
LazySingletonSafe lazySafe2 = LazySingletonSafe.getInstance();
System.out.println("懒汉式单例(线程安全,同步方法)是否为同一实例: " + (lazySafe1 == lazySafe2));
lazySafe1.showMessage();
// 测试懒汉式单例(线程安全,双重检查锁定)
LazySingletonDoubleCheck lazyDoubleCheck1 = LazySingletonDoubleCheck.getInstance();
LazySingletonDoubleCheck lazyDoubleCheck2 = LazySingletonDoubleCheck.getInstance();
System.out.println("懒汉式单例(线程安全,双重检查锁定)是否为同一实例: " + (lazyDoubleCheck1 == lazyDoubleCheck2));
lazyDoubleCheck1.showMessage();
// 测试静态内部类单例
StaticInnerClassSingleton staticInner1 = StaticInnerClassSingleton.getInstance();
StaticInnerClassSingleton staticInner2 = StaticInnerClassSingleton.getInstance();
System.out.println("静态内部类单例是否为同一实例: " + (staticInner1 == staticInner2));
staticInner1.showMessage();
// 测试枚举单例
EnumSingleton enum1 = EnumSingleton.INSTANCE;
EnumSingleton enum2 = EnumSingleton.INSTANCE;
System.out.println("枚举单例是否为同一实例: " + (enum1 == enum2));
enum1.showMessage();
执行流程
- 调用各种单例类的
getInstance()方法获取实例 - 比较两次获取的实例是否为同一实例
- 调用实例的
showMessage()方法显示信息
运行说明
编译并运行测试类
# 进入单例模式目录
cd singleton
# 编译所有Java文件
javac *.java
# 运行测试类
java SingletonTest
预期输出
饿汉式单例是否为同一实例: true
饿汉式单例模式
懒汉式单例(线程不安全)是否为同一实例: true
懒汉式单例模式(线程不安全)
懒汉式单例(线程安全,同步方法)是否为同一实例: true
懒汉式单例模式(线程安全,同步方法)
懒汉式单例(线程安全,双重检查锁定)是否为同一实例: true
懒汉式单例模式(线程安全,双重检查锁定)
静态内部类单例是否为同一实例: true
静态内部类单例模式
枚举单例是否为同一实例: true
枚举单例模式
优点
- 确保一个类只有一个实例,避免了频繁创建和销毁对象的开销
- 提供了全局访问点,方便访问唯一实例
- 可以延迟加载,提高系统的启动速度
- 可以严格控制实例的创建过程
缺点
- 单例模式可能导致代码耦合度增加,因为所有使用单例的类都依赖于它
- 单例模式可能导致测试困难,因为单例的状态会影响所有测试用例
- 单例模式可能导致并发问题,需要特别注意线程安全
- 单例模式违反了单一职责原则,因为它既负责对象的创建,又负责对象的业务逻辑
适用场景
- 需要确保全局只有一个实例的场景,如配置管理、日志记录、数据库连接池等
- 需要频繁创建和销毁对象的场景,如线程池、缓存等
- 需要严格控制实例数量的场景,如硬件资源访问
- 需要提供全局访问点的场景,如系统服务、工具类等
- 需要延迟加载以提高系统启动速度的场景
浙公网安备 33010602011771号