当前位置: 首页 > news >正文

从双重检查锁定的设计意图、锁的作用、第一次检查提升性能的原理三个角度,详细拆解单例模式的逻辑

public class SFTPUtil {// 16 usages(注释为截图中的使用统计,实际代码无需保留)private static ChannelSftp sftp;// 6 usages(注释为截图中的使用统计,实际代码无需保留)private volatile static SFTPUtil instance = null;// 1 usage(注释为截图中的使用统计,实际代码无需保留),私有构造方法,防止外部直接实例化private SFTPUtil() {}public static SFTPUtil getInstance(String host, int port, String username, String password) {// 日志打印 instance 是否为 null,{} 是占位符,实际会替换为 true/falselog.info("---------> instance == null :{}", instance == null);if (instance == null) {// 类级别的同步锁,保证多线程下仅初始化一次synchronized (SFTPUtil.class) {if (instance == null) {// 创建 SFTPUtil 实例instance = new SFTPUtil();// 获取 SFTP 连接,并赋值给静态变量 sftpsftp = instance.connect(host, port, username, password);}}}return instance;}// 截图中未展示 connect 方法的具体实现,需补充(根据上下文推测是建立 ChannelSftp 连接的逻辑)private ChannelSftp connect(String host, int port, String username, String password) {// 这里需补充实际连接 SFTP 服务器的代码,示例如下(基于 JSch 库):ChannelSftp channelSftp = null;try {JSch jsch = new JSch();Session session = jsch.getSession(username, host, port);session.setPassword(password);// 跳过严格主机密钥检查(测试环境可用,生产环境需配置 known_hosts)session.setConfig("StrictHostKeyChecking", "no"); session.connect();Channel channel = session.openChannel("sftp");channel.connect();channelSftp = (ChannelSftp) channel;} catch (JSchException e) {// 实际需完善异常处理,比如日志记录或抛出自定义异常log.error("SFTP 连接失败", e);}return channelSftp;}// 若有其他工具方法(如下载、上传等),需补充...public downLoadZipMethod(){// 执行下载 ".ok" 文件
SFTPUtil sf = SFTPUtil.getInstance(props.getEgjSftpHost(), Integer.parseInt(props.getEgjSftpPort()), props.getEgjSftpUsername(), props.getEgjSftpPassword()
);
}
}

这块代码是什么时候会调用会每次调用downloadZipFile都会调用这个SFTPUtil.getInstance(),走到第二个图的逻辑,第二个图中每一次都会返回一个新的SFTPUtil实例吗?第二个图中所加的锁是什么作用?

以下从 实例创建逻辑锁的作用是否每次返回新实例 三个维度详细分析:

1. 实例创建逻辑:是否每次返回新实例?

不会每次返回新实例,因为代码用了 单例模式(双重检查锁定),核心逻辑:

private volatile static SFTPUtil instance = null;public static SFTPUtil getInstance(...) {if (instance == null) { // 第一次检查(无锁,提升性能)synchronized (SFTPUtil.class) { // 加类锁,保证线程安全if (instance == null) { // 第二次检查(避免多线程重复创建)instance = new SFTPUtil(); // 仅第一次调用时创建实例sftp = instance.connect(...); }}}return instance; // 后续调用直接返回已创建的实例
}
  • 结论:无论调用 getInstance 多少次,全局仅会创建 1 个 SFTPUtil 实例(单例特性)。

2. 锁的作用:synchronized (SFTPUtil.class)

synchronized (SFTPUtil.class)类级别的锁,作用是:

  • 保证线程安全:在多线程环境下,避免多个线程同时进入 if (instance == null) 逻辑,导致重复创建 SFTPUtil 实例。
  • 实现单例的核心:通过双重检查(if (instance == null) 两次判断) + 类锁,确保 instance = new SFTPUtil() 仅执行一次。

3. downloadZipFile 调用 getInstance 的时机

每次调用 downloadZipFile 方法时,都会执行:

SFTPUtil sf = SFTPUtil.getInstance(...);

但由于 getInstance 是单例逻辑,只有第一次调用 会触发 new SFTPUtil()sftp = instance.connect(...);后续调用直接返回已存在的 instance

潜在问题与优化建议

(1)单例与多环境冲突

如果 getInstance 的入参(hostport 等)在不同调用中变化,但实例已创建,会导致后续调用使用第一次的参数(比如第一次用 host=192.168.1.1 创建实例,后续传入 host=10.0.0.1 也不会重新创建实例)。

风险:若业务需要连接不同 SFTP 服务器,单例模式会导致参数失效。

解决方案

  • 放弃单例,改为每次创建新实例(去掉 instance 静态变量,直接 new SFTPUtil() );
  • 或根据入参(hostport 等)维护多个单例(用 Map 缓存不同参数对应的实例)。
(2)sftp 静态变量的线程安全

private static ChannelSftp sftp; 是静态变量,若多线程同时使用 sftp 执行操作(如 cdget ),会引发线程安全问题(不同线程操作同一个 ChannelSftp 可能导致命令混乱)。

解决方案

  • sftp 改为实例变量(非静态),每个 SFTPUtil 实例持有独立的 sftp 通道;
  • 或对 sftp 的操作加锁(synchronized 或使用线程安全的工具类)。
(3)连接未释放的风险

SFTPUtil 是单例且长期运行,sftp 通道可能因超时、网络波动断开,且没有重连逻辑,会导致后续操作失败。

解决方案

  • 添加连接校验与重连逻辑(在 download 前检查 sftp.isConnected() ,断开则重新 connect );
  • 实现 close 方法,在业务结束时主动释放连接。

总结

  • 是否每次返回新实例:否,全局仅 1 个实例(单例模式)。
  • 锁的作用:保证多线程下仅创建 1 个实例,线程安全。
  • 潜在问题:单例与多参数冲突、sftp 静态变量线程不安全、连接未释放。

若业务需要连接不同 SFTP 服务器,或多线程操作 sftp ,建议调整单例逻辑或增加线程安全处理。

http://www.dtcms.com/a/350961.html

相关文章:

  • 基于SpringBoot的网上点餐系统
  • OpenTenBase核心技术解密:突破OLTP与OLAP边界的分布式数据库革新
  • Flask模块如何使用
  • 浅谈技术顾问的转型困境
  • Web前端开发基础
  • windows 下 使用C++ 集成 zenoh
  • ArcGIS学习-9 ArcGIS查询操作
  • vLLM轻松通
  • ArcGIS学习-10 空间连接和矢量分析
  • 百度IOS客户端岗位--面试真题分析
  • 5G 三卡图传终端:应急救援管理的 “可视化指挥核心”
  • PostgreSQL诊断系列(3/6):性能瓶颈定位——缓冲池、I/O与临时文件
  • 【Linux系统】线程概念
  • 【R代码分析】甲烷排放绘制代码-参考论文
  • 【云原生】CentOS安装Kubernetes+Jenkins
  • RAW API 的 UDP 总结1
  • Android Glide常见问题解决方案:从图片加载到内存优化
  • [在实践中学习] 中间件理论和方法--Redis
  • 【JavaEE】了解volatile和wait、notify(三)
  • 08-引入AI编程插件(TRAE)编写单元测试并检查覆盖率
  • Cloudflare 推出 GenAI 安全工具,守护企业数据
  • 科普 | 5G支持的WWC架构是个啥(1)?
  • android 改机系列之-虚拟摄像头-替换相机预览画面
  • excel导出,多列合并表头
  • 运行node18报错
  • 【JVM】类加载器都有哪些?
  • ChromaDB向量数据库Python教程:从入门到实战完整指南(含游戏资源管理系统案例
  • Java大厂面试实战:从Spring Boot到微服务架构的全链路技术拆解
  • 计算机视觉:从 “看见” 到 “理解”,解锁机器感知世界的密码
  • Windows 命令行:mkdir 命令