Yunyuzuiluo

单例模式

单例模式

模式简介

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。单例模式通过私有化构造方法、提供静态方法或静态变量来实现唯一实例的创建和访问。
单例模式适用于需要确保全局只有一个实例的场景,如配置管理、日志记录、数据库连接池等。

懒汉型 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();

执行流程

  1. 调用各种单例类的 getInstance() 方法获取实例
  2. 比较两次获取的实例是否为同一实例
  3. 调用实例的 showMessage() 方法显示信息

运行说明

编译并运行测试类

# 进入单例模式目录
cd singleton

# 编译所有Java文件
javac *.java

# 运行测试类
java SingletonTest

预期输出

饿汉式单例是否为同一实例: true
饿汉式单例模式
懒汉式单例(线程不安全)是否为同一实例: true
懒汉式单例模式(线程不安全)
懒汉式单例(线程安全,同步方法)是否为同一实例: true
懒汉式单例模式(线程安全,同步方法)
懒汉式单例(线程安全,双重检查锁定)是否为同一实例: true
懒汉式单例模式(线程安全,双重检查锁定)
静态内部类单例是否为同一实例: true
静态内部类单例模式
枚举单例是否为同一实例: true
枚举单例模式

优点

  1. 确保一个类只有一个实例,避免了频繁创建和销毁对象的开销
  2. 提供了全局访问点,方便访问唯一实例
  3. 可以延迟加载,提高系统的启动速度
  4. 可以严格控制实例的创建过程

缺点

  1. 单例模式可能导致代码耦合度增加,因为所有使用单例的类都依赖于它
  2. 单例模式可能导致测试困难,因为单例的状态会影响所有测试用例
  3. 单例模式可能导致并发问题,需要特别注意线程安全
  4. 单例模式违反了单一职责原则,因为它既负责对象的创建,又负责对象的业务逻辑

适用场景

  1. 需要确保全局只有一个实例的场景,如配置管理、日志记录、数据库连接池等
  2. 需要频繁创建和销毁对象的场景,如线程池、缓存等
  3. 需要严格控制实例数量的场景,如硬件资源访问
  4. 需要提供全局访问点的场景,如系统服务、工具类等
  5. 需要延迟加载以提高系统启动速度的场景

posted on 2025-12-17 23:54  刘晋宇  阅读(8)  评论(0)    收藏  举报

导航