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.getSftpHost(), Integer.parseInt(props.getSftpPort()), props.getSftpUsername(), props.getSftpPassword()
);
}
}
這塊代碼是什么時候會調用會每次調用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
的入參(host
、port
等)在不同調用中變化,但實例已創建,會導致后續調用使用第一次的參數(比如第一次用 host=192.168.1.1
創建實例,后續傳入 host=10.0.0.1
也不會重新創建實例)。
風險:若業務需要連接不同 SFTP 服務器,單例模式會導致參數失效。
解決方案:
- 放棄單例,改為每次創建新實例(去掉
instance
靜態變量,直接new SFTPUtil()
); - 或根據入參(
host
、port
等)維護多個單例(用Map
緩存不同參數對應的實例)。
(2)sftp
靜態變量的線程安全
private static ChannelSftp sftp;
是靜態變量,若多線程同時使用 sftp
執行操作(如 cd
、get
),會引發線程安全問題(不同線程操作同一個 ChannelSftp
可能導致命令混亂)。
解決方案:
- 將
sftp
改為實例變量(非靜態),每個SFTPUtil
實例持有獨立的sftp
通道; - 或對
sftp
的操作加鎖(synchronized
或使用線程安全的工具類)。
(3)連接未釋放的風險
若 SFTPUtil
是單例且長期運行,sftp
通道可能因超時、網絡波動斷開,且沒有重連邏輯,會導致后續操作失敗。
解決方案:
- 添加連接校驗與重連邏輯(在
download
前檢查sftp.isConnected()
,斷開則重新connect
); - 實現
close
方法,在業務結束時主動釋放連接。
總結
- 是否每次返回新實例:否,全局僅 1 個實例(單例模式)。
- 鎖的作用:保證多線程下僅創建 1 個實例,線程安全。
- 潛在問題:單例與多參數沖突、
sftp
靜態變量線程不安全、連接未釋放。
若業務需要連接不同 SFTP 服務器,或多線程操作 sftp
,建議調整單例邏輯或增加線程安全處理。