IT评测·应用市场-qidao123.com
标题:
懒汉式单例模式
[打印本页]
作者:
勿忘初心做自己
时间:
2025-1-17 00:19
标题:
懒汉式单例模式
懒汉式单例是一种在需要时才会初始化实例的单例模式实现方式,适用于需要延迟加载的场景。以下是一个现实使用懒汉式单例的例子,并结合适用场景进行分析。
示例场景:日志管理器
在开发过程中,日志记录是一个常见需求,通常日志记录器在整个应用中只需要一个实例。使用懒汉式单例可以确保日志管理器只在第一次需要时进行初始化,从而节省体系资源。
懒汉式单例完整代码
public class LogManager {
// 1. 静态变量,保存唯一实例,但不立即初始化
private static LogManager instance = null;
// 2. 私有构造方法,防止外部实例化
private LogManager() {
System.out.println("LogManager initialized!");
}
// 3. 提供一个静态方法访问唯一实例
public static synchronized LogManager getInstance() {
if (instance == null) {
instance = new LogManager(); // 延迟实例化
}
return instance;
}
// 4. 示例方法,用于记录日志
public void log(String message) {
System.out.println("Log: " + message);
}
}
复制代码
代码分析
静态变量 instance:
静态变量 instance 用于保存 LogManager 类的唯一实例。
初始值为 null,实例化操纵延后到第一次调用 getInstance() 时才进行。
私有构造方法:
构造方法被声明为 private,防止外部通过 new LogManager() 创建实例。
在构造方法中可以放置初始化逻辑,例如配置日志文件路径等。
静态方法 getInstance():
是懒汉式单例的核心,通过 synchronized 关键字包管线程安全。
第一次调用时,instance 为 null,会创建一个新的实例;
后续调用时,直接返回已经创建的实例。
功能性方法 log():
提供详细的业务功能,例如记录日志。
使用示例
假设我们需要记录一些紧张的操纵日志,可以通过以下代码来使用 LogManager:
public class Main {
public static void main(String[] args) {
// 第一次调用时实例化 LogManager
LogManager logger1 = LogManager.getInstance();
logger1.log("This is the first log message.");
// 第二次调用时直接返回已有实例
LogManager logger2 = LogManager.getInstance();
logger2.log("This is the second log message.");
// 比较两个实例
System.out.println("Are logger1 and logger2 the same instance? " + (logger1 == logger2));
}
}
复制代码
输出结果
LogManager initialized!
Log: This is the first log message.
Log: This is the second log message.
Are logger1 and logger2 the same instance? true
复制代码
说明:
第一次调用 LogManager.getInstance() 时,LogManager 被初始化(输出 "LogManager initialized!")。
第二次调用 getInstance() 时,只是返回已有实例,没有再次创建新实例。
比较两个实例,结果为 true,表明它们是同一个对象。
懒汉式单例的优缺点
优点
延迟加载
:
实例在第一次使用时才创建,节省内存和体系资源。
线程安全性
:
使用 synchronized 包管线程安全。
缺点
性能问题
:
每次调用 getInstance() 都需要进入同步块,会带来一定的性能开销。在性能敏感的场景下可能不敷高效。
改进方案:双重查抄锁定(Double-Checked Locking)
为了办理同步带来的性能问题,可以使用双重查抄锁定优化懒汉式单例:
public class LogManager {
// 1. 静态变量,使用 volatile 修饰以保证可见性
private static volatile LogManager instance = null;
// 2. 私有构造方法
private LogManager() {
System.out.println("LogManager initialized!");
}
// 3. 提供静态方法,使用双重检查锁定
public static LogManager getInstance() {
if (instance == null) { // 第一次检查
synchronized (LogManager.class) {
if (instance == null) { // 第二次检查
instance = new LogManager();
}
}
}
return instance;
}
// 示例功能
public void log(String message) {
System.out.println("Log: " + message);
}
}
复制代码
优势
第一次查抄和第二次查抄减少了不必要的同步,提高了性能。
使用 volatile 关键字包管多线程环境下的可见性,防止指令重排序导致的错误。
扩展1 — 双重查抄锁定
这段代码是一个双重查抄锁定(Double-Checked Locking)实现的懒汉式单例模式的核心部分,它旨在办理多线程环境下单例实例创建的线程安全问题,同时优化性能。下面是对这段代码的详细分析:
第一次查抄 (if (instance == null)):
目的:快速判定实例是否已经创建。
优点:假如实例已经存在,则可以直接返回实例,避免进入同步块,提高了性能。
同步块 (synchronized (LogManager.class)):
目的:包管在多线程环境下只有一个线程能够进入块内创建实例,确保线程安全。
synchronized用在类对象上,确保同一时候只有一个线程可以初始化实例。
第二次查抄 (if (instance == null)):
目的:在同步块内再次查抄实例是否为 null。
原因:在第一次查抄之后进入同步块之前,可能有其他线程已经创建了实例,因此需要再次查抄以防止重复创建。
实例化 (instance = new LogManager()):
当且仅当 instance 确实为 null 且当前线程得到了同步锁时,才创建实例。
确保 LogManager 的实例只被创建一次。
返回实例 (return instance):
无论是通过快速路径(无锁)照旧同步路径(加锁),终极都会返回唯一的实例。
Why Double-Checked Locking?
性能优化
:
通过双重查抄,减少了进入同步锁的次数。只有在 instance 为 null 时,才会进入同步块。一般环境下(即实例已经创建后),只需要执行第一次查抄即可返回实例,无需同步。
线程安全
:
同步块包管了只有一个线程可以执行实例初始化。纵然多个线程同时发现 instance 为 null,由于同步的存在,终极只有一个线程能够创建实例。
扩展2 — 使用 volatile
为了完全包管双重查抄锁定的精确性,instance 应该使用 volatile 关键字声明:
private static volatile LogManager instance = null;
复制代码
作用
:
防止指令重排序1:确保 new LogManager() 操纵的顺序精确,即先分配内存,再执行构造函数,最后将内存地址赋值给 instance。
变量在多个线程之间的可见性:一旦一个线程修改了 instance,其他线程能够立即看到变化。
双重查抄锁定模式是实现懒汉式单例的一种高效方式,适用于性能要求较高的多线程环境。但是,它的精确实现依赖于 volatile 关键字来防止重排序问题。通过这种方式,我们可以在包管线程安全的同时,尽量减少同步带来的性能损耗。
总结
懒汉式单例适合于需要延迟加载且实例化成本较高的场景(如日志管理器、配置加载器等)。在并发场景下,最好使用线程安全的实现,例如同步方法版或双重查抄锁定版,以确保唯一实例的精确性和性能的平衡。
指令重排序:编译器和处理器在执行程序时可能会对指令进行重排序,以优化性能。volatile 关键字会禁止这种重排序,确保变量的初始化和其他操纵的执行顺序符合程序的预期。
这对于实现线程安全的懒汉式单例模式非常紧张,因为它包管了对象在初始化完成后才会被其他线程看到。 ↩︎
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/)
Powered by Discuz! X3.4