1. 單例模式的實現方式及線程安全
單例模式(Singleton Pattern)確保一個類只有一個實例,并提供一個全局訪問點。以下是常見的單例模式實現方式,以及如何保證線程安全:
單例模式的實現方式
-
餓漢式(Eager Initialization)
-
實現:在類加載時就創建實例(靜態初始化)。
-
代碼示例
public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}}
-
線程安全:天生線程安全,因為實例在類加載時創建,JVM保證類加載過程是線程安全的。
-
優缺點:簡單,但可能會導致資源浪費(如果實例未被使用)。
-
-
懶漢式(Lazy Initialization)
-
實現:在第一次調用時創建實例。
-
代碼示例(非線程安全)
public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}}
-
線程安全問題:多線程環境下,可能多個線程同時判斷instance == null,導致多次創建實例。
-
改進(加鎖)
public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;} }
- 使用synchronized關鍵字保證線程安全,但鎖粒度較大,性能較低。
-
-
雙重檢查鎖(Double-Checked Locking)
-
實現:在懶漢式基礎上優化,使用雙重檢查和volatile關鍵字。
-
代碼示例
收起自動換行
public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;} }
-
線程安全:volatile防止指令重排序,確保實例初始化完成前其他線程不會訪問;雙重檢查減少鎖的開銷。
-
優缺點:性能較高,但代碼稍復雜。
-
-
靜態內部類(Static Inner Class)
-
實現:利用靜態內部類的延遲加載特性。
-
代碼示例
public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}}
-
線程安全:JVM保證靜態內部類加載時是線程安全的,且只有在調用getInstance時才加載SingletonHolder,實現懶加載。
-
優缺點:兼顧懶加載和線程安全,推薦使用。
-
-
枚舉單例(Enum Singleton)
-
實現:利用Java枚舉類型的特性。
-
代碼示例
public enum Singleton {INSTANCE;public void doSomething() {// 業務邏輯}}
-
線程安全:JVM保證枚舉的實例化是線程安全的,且能防止反射和序列化破壞單例。
-
優缺點:簡潔、安全,但不適合復雜的初始化邏輯。
-
保證線程安全的關鍵點
-
餓漢式和枚舉:天生線程安全,依賴JVM類加載機制。
-
懶漢式:需加鎖(如synchronized)或使用雙重檢查鎖。
-
雙重檢查鎖:結合volatile和synchronized,防止指令重排序和多線程競爭。
-
靜態內部類:利用JVM類加載機制,延遲加載且線程安全。
-
序列化和反射攻擊
-
防止反射:構造函數拋出異常或使用枚舉。
-
防止序列化破壞:在類中添加
readResolve
-
private Object readResolve() {return instance;
2. 策略模式(Strategy Pattern)
定義
策略模式是一種行為型設計模式,定義一系列算法(策略),將每個算法封裝起來,并使它們可以互換。客戶端可以根據需要選擇不同的策略,而不改變調用代碼。
核心組成
- 抽象策略接口(Strategy):定義算法的接口。
- 具體策略類(ConcreteStrategy):實現具體算法。
- 上下文類(Context):持有策略接口的引用,負責調用具體策略。
代碼示例
// 策略接口interface Strategy {int execute(int a, int b);
}// 具體策略:加法
class AddStrategy implements Strategy {@Overridepublic int execute(int a, int b) {return a + b;}
}// 具體策略:減法class SubtractStrategy implements Strategy {@Overridepublic int execute(int a, int b) {return a - b;}
}// 上下文class Context {private Strategy strategy;public void setStrategy(Strategy strategy) {this.strategy = strategy;}public int executeStrategy(int a, int b) {return strategy.execute(a, b);}
}// 使用public class Main {public static void main(String[] args) {Context context = new Context();context.setStrategy(new AddStrategy());System.out.println(context.executeStrategy(5, 3)); // 輸出 8context.setStrategy(new SubtractStrategy());System.out.println(context.executeStrategy(5, 3)); // 輸出 2}}
使用場景
-
多種算法或行為:當一個類有多種行為,且這些行為可以根據上下文動態切換時(如支付方式:微信、支付寶、銀行卡)。
-
避免條件語句:替代大量if-else或switch語句,使代碼更清晰。
-
算法獨立性:需要將算法與客戶端代碼解耦,方便擴展和維護。
-
典型案例
- 排序算法選擇(如快速排序、歸并排序)。
- 支付系統(不同支付方式)。
- 游戲中的角色技能(不同技能效果)。
優缺點
- 優點:靈活、可擴展,符合開閉原則;代碼復用性高。
- 缺點:客戶端需要了解所有策略;策略類可能較多。
3. 模板方法模式(Template Method Pattern)
定義
模板方法模式是一種行為型設計模式,定義一個操作的算法骨架,將某些步驟延遲到子類實現。父類控制算法流程,子類提供具體實現。
核心組成
- 抽象模板類(AbstractClass):定義算法骨架(模板方法)和抽象方法。
- 具體子類(ConcreteClass):實現抽象方法,提供具體邏輯。
代碼示例
// 抽象模板類abstract class AbstractClass {// 模板方法,定義算法骨架public final void templateMethod() {step1();step2();step3();}protected abstract void step1();protected abstract void step2();protected void step3() { // 可選的鉤子方法 也就是子類可以選擇性進行重寫,不重寫默認執行父類方法System.out.println("Default step3");}
}// 具體子類class ConcreteClass extends AbstractClass {@Overrideprotected void step1() {System.out.println("ConcreteClass: Step 1");}@Overrideprotected void step2() {System.out.println("ConcreteClass: Step 2");}@Overrideprotected void step3() {System.out.println("ConcreteClass: Custom Step 3");}
}// 使用public class Main {public static void main(String[] args) {AbstractClass process = new ConcreteClass();process.templateMethod();}}
使用場景
-
固定算法骨架:當多個類共享相同的算法流程,但部分步驟的實現不同(如數據處理流程:讀取、處理、保存)。
-
代碼復用:通過父類定義公共邏輯,子類只實現差異化部分。
-
控制子類擴展:通過final模板方法限制子類修改算法結構。
-
典型案例
- 框架中的生命周期方法(如Spring的ApplicationContext初始化)。
- 游戲開發中關卡流程(加載、運行、結束)。
- 報表生成(數據采集、格式化、輸出)。
優缺點
-
優點:提高代碼復用性;算法結構統一,易于維護;符合開閉原則。
-
缺點:子類數量可能增多;父類設計復雜時可能限制靈活性。
使用場景 適合抽象類 適合接口 需要代碼復用 ? 適合,支持方法和成員變量實現 ? 不適合(除非 default 方法) 表示“是什么”(is-a) ? 抽象類適合建層次結構 ? 接口更適合“能做什么” 表示“能做什么”(has ability to) ? ? 非常適合,比如 Serializable
,Runnable
要求多個類共享部分邏輯 ? 用抽象類抽取通用部分 ? 接口不適合寫邏輯實現 實現多個類型/能力組合 ? 不能多繼承 ? 接口天生支持多繼承
總結對比
- 單例模式:確保單一實例,關注對象創建,需考慮線程安全(如雙重檢查鎖、靜態內部類)。
- 策略模式:關注行為切換,適合動態選擇算法,解耦客戶端與算法實現。
- 模板方法模式:關注算法骨架,適合固定流程但細節可變,強調繼承和復用。
拓展:責任鏈模式(Chain of Responsibility Pattern)是一種行為型設計模式,用于將請求的發送者和接收者解耦,使多個對象都有機會處理這個請求。該模式將處理請求的對象組成一條鏈,請求沿著這條鏈傳遞,直到被某個對象處理為止。
結構圖(類圖)
Client --> Handler1 --> Handler2 --> Handler3 --> …
核心類圖包括:
Handler(抽象處理者)
定義處理請求的接口。
持有下一個處理者的引用。
ConcreteHandler(具體處理者)
實現請求處理的邏輯。
如果自己不能處理,則將請求轉發給下一個處理者。
Client(客戶端)
創建處理鏈,并將請求傳入第一個處理者。
** 應用場景
審批流程(如:員工請假、軟件發布等)**
Java Web 的 Filter 過濾器鏈
Spring Security 的認證授權鏈
Netty 的 ChannelPipeline
🧑?💻 Java 示例(審批流程)
比如一個請假流程,組長可以審批 1 天,經理可以審批 3 天,總監可以審批 7 天:
抽象處理者
```java
public abstract class LeaveHandler {protected LeaveHandler next;public void setNext(LeaveHandler next) {this.next = next;}public abstract void handleRequest(int days);
}
具體處理者
public class TeamLeader extends LeaveHandler {@Overridepublic void handleRequest(int days) {if (days <= 1) {System.out.println("組長審批了 " + days + " 天的假期");} else if (next != null) {next.handleRequest(days);}}
}public class Manager extends LeaveHandler {@Overridepublic void handleRequest(int days) {if (days <= 3) {System.out.println("經理審批了 " + days + " 天的假期");} else if (next != null) {next.handleRequest(days);}}
}public class Director extends LeaveHandler {@Overridepublic void handleRequest(int days) {if (days <= 7) {System.out.println("總監審批了 " + days + " 天的假期");} else {System.out.println("假期太長,不批準");}}
}
客戶端調用
public class Client {public static void main(String[] args) {LeaveHandler teamLeader = new TeamLeader();LeaveHandler manager = new Manager();LeaveHandler director = new Director();teamLeader.setNext(manager);manager.setNext(director);teamLeader.handleRequest(2); // 輸出:經理審批了 2 天的假期teamLeader.handleRequest(6); // 輸出:總監審批了 6 天的假期teamLeader.handleRequest(10); // 輸出:假期太長,不批準}
}
1.責任鏈模式的優點?
解耦請求發送者和接收者。
動態調整處理鏈,靈活性高。
單一職責,每個處理者專注特定請求。
2.缺點?
請求可能未被處理。
鏈過長影響性能。
調試復雜。
3.使用場景?
日志系統(如不同級別日志處理)。
事件處理(如 GUI 事件傳遞)。
審批流程(如逐級審批)。
4.如何避免請求未被處理?
設置默認處理者。
確保鏈配置完整。