论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
应用中心
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
qidao123.com技术社区-IT企服评测·应用市场
»
论坛
›
软件与程序人生
›
DevOps与敏捷开发
›
懒汉式单例模式
懒汉式单例模式
勿忘初心做自己
论坛元老
|
2025-1-17 00:19:46
|
显示全部楼层
|
阅读模式
楼主
主题
1777
|
帖子
1777
|
积分
5331
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
懒汉式单例是一种在需要时才会初始化实例的单例模式实现方式,适用于需要延迟加载的场景。以下是一个现实使用懒汉式单例的例子,并结合适用场景进行分析。
示例场景:日志管理器
在开发过程中,日志记录是一个常见需求,通常日志记录器在整个应用中只需要一个实例。使用懒汉式单例可以确保日志管理器只在第一次需要时进行初始化,从而节省体系资源。
懒汉式单例完整代码
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企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
勿忘初心做自己
论坛元老
这个人很懒什么都没写!
楼主热帖
新一代企业级安全OneDNS
记一次有意思的业务实现 → 单向关注是 ...
集合论第6-8章
将git仓库从submodule转换为subtree ...
redis 工具类
DNS协议
Java 代理模式
Python 开发 漏洞的批量搜索与利用.(G ...
[kubernetes]服务健康检查
反射 p1 反射机制
标签云
国产数据库
集成商
AI
运维
CIO
存储
服务器
浏览过的版块
Oracle
快速回复
返回顶部
返回列表