- 臨近秋招,備戰暑期實習,祝大家每天進步億點點!Day16
- 本篇總結的是 單例設計模式,后續會每日更新~

1、簡介
- 單例模式使?場景:
- 業務系統全域只需要?個物件實體,?如發號器、 redis 連接物件等,
- Spring IOC容器中的 Bean 默認就是單例,
- Spring Boot 中的 Controller、Service、Dao 層中通過
@Autowire的依賴注?物件默認都是單例的,
- 單例模式分類:
- 懶漢:就是所謂的懶加載,延遲創建物件,需要用的時候再創建物件,
- 餓漢:與懶漢相反,提前創建物件,
- 單例模式實作步驟:
- 私有化建構式,
- 提供獲取單例的?法,
2、單例模式——懶漢式
單例模式——懶漢式有以下?種實作?式:
/**
* @Auther: csp1999
* @Date: 2020/11/06/20:36
* @Description: 單例設計模式-懶漢式
*/
public class SingletonLazy {
// 當需要用到該實體的時候再創建實體物件
private static SingletonLazy instance;
/**
* 建構式私有化
* 不能通過 new SingletonLazy() 的方式創建實體
*
* 當需要用到該實體的時候在加載
* 只能通過 SingletonLazy.getInstance() 這種方式獲取實體
*/
private SingletonLazy() {
}
/**
* 單例物件的方法
*/
public void process() {
System.out.println("方法實體化成功!");
}
/**
* 方式一:
* <p>
* 對外暴露一個方法獲取該類的物件
* <p>
* 缺點:執行緒不安全,多執行緒下存在安全問題
*
* @return
*/
public static SingletonLazy getInstance() {
if (instance == null) {// 實體為null時候才創建
/**
* 執行緒安全問題:
* 當某一時刻,兩個或多個執行緒同時判斷到instance == null成立的時候
* 這些執行緒同時進入該if判斷內部執行實體化
* 則會新建出不止一個SingletonLazy實體
*/
instance = new SingletonLazy();// 當需要的時候再進行實體化物件
}
return instance;
}
/**
* 方式二:
* 通過加synchronized鎖 保證執行緒安全
*
* 采用synchronized 對方法加鎖有很大的性能開銷
* 因為當getInstance2()內部邏輯比較復雜的時候,在高并發條件下
* 沒獲取到加鎖方法執行權的執行緒,都得等到這個方法內的復雜邏輯執行完后才能執行,等待浪費時間,效率比較低
*
* @return
*/
public static synchronized SingletonLazy getInstance2() {
if (instance == null) {// 實體為null時候才創建
// 方法上加synchronized鎖后可以保證執行緒安全
instance = new SingletonLazy();// 當需要的時候再進行實體化物件
}
return instance;
}
/**
* 方式三:
* 在getInstance3()方法內,針對區域需要加鎖的代碼塊加鎖,而不是給整個方法加鎖
*
* 也存在缺陷:
* @return
*/
public static SingletonLazy getInstance3() {
if (instance == null) {// 實體為null時候才創建
// 區域加鎖后可以保證執行緒安全,效率較高
// 缺陷:假設執行緒A和執行緒B
synchronized (SingletonLazy.class){
// 當執行緒A獲得鎖的執行權的時候B等待 A執行new SingletonLazy();實體化
// 當A執行緒執行完畢后,B再獲得執行權,這時候還是可以實體化該物件
instance = new SingletonLazy();// 當需要的時候再進行實體化物件
}
}
return instance;
}
}
單例模式:懶漢實作 + 雙重檢查鎖定 + 記憶體模型
對于上面方式三存在的缺陷,我們可以使用雙重檢查鎖定的方式對其進行改進:
/**
* 方式三改進版本:
* 在getInstance3()方法內,針對區域需要加鎖的代碼塊加鎖,而不是給整個方法加鎖
*
* DCL 雙重檢查鎖定 (Double-Checked-Locking) 在多執行緒情況下保持高性能
*
* 這是否安全? instance = new SingletonLazy(); 并不是原子性操作
* jvm中 instance實體化記憶體模型流程如下:
* 1.分配空間給物件
* 2.在空間內創建物件
* 3.將物件賦值給instance參考
*
* 假如出現如下順序錯亂的情況:
* 執行緒的執行順序為:1 -> 3 -> 2, 那么這時候會把值寫回主記憶體
* 則,其他執行緒就會讀取到instance的最新值,但是這個是不完全的物件
* (指令重排現象)
*
* @return
*/
public static SingletonLazy getInstance3plus() {
if (instance == null) {// 實體為null時候才創建
// 區域加鎖后可以保證執行緒安全,效率較高
// 假設執行緒A和執行緒B
synchronized (SingletonLazy.class){// 第一重檢查
// 當執行緒A獲得鎖的執行權的時候B等待 A執行new SingletonLazy();實體化
// 當A執行緒執行完畢后,B再獲得執行權,這時候再判斷instance == null是否成立
// 如果不成立,B執行緒無法 實體化SingletonLazy
if (instance == null){// 第二重檢查
instance = new SingletonLazy();// 當需要的時候再進行實體化物件
}
}
}
return instance;
}
再次升級方式三,來解決記憶體模型中的指令重排問題:
// 添加volatile 關鍵字,禁止實體化物件時,記憶體模型中出現指令重排現象
private static volatile SingletonLazy instance;
/**
* 方式三再次升級版本:
* 在getInstance3()方法內,針對區域需要加鎖的代碼塊加鎖,而不是給整個方法加鎖
*
* DCL 雙重檢查鎖定 (Double-Checked-Locking) 在多執行緒情況下保持高性能
*
* 解決指令重排問題——禁止指令重排
* @return
*/
public static SingletonLazy getInstance3plusplus() {
if (instance == null) {// 實體為null時候才創建
// 區域加鎖后可以保證執行緒安全,效率較高
// 假設執行緒A和執行緒B
synchronized (SingletonLazy.class){// 第一重檢查
// 當執行緒A獲得鎖的執行權的時候B等待 A執行new SingletonLazy();實體化
// 當A執行緒執行完畢后,B再獲得執行權,這時候再判斷instance == null是否成立
// 如果不成立,B執行緒無法 實體化SingletonLazy
if (instance == null){// 第二重檢查
instance = new SingletonLazy();// 當需要的時候再進行實體化物件
}
}
}
return instance;
}
單例模式——懶漢式呼叫:
@Test
public void testSingletonLazy(){
SingletonLazy.getInstance().process();
}
3、單例模式——餓漢式
/**
* @Auther: csp1999
* @Date: 2020/11/06/21:39
* @Description: 單例設計模式-餓漢式
*/
public class SingletonHungry {
// 當類加載的時候就直接實體化物件
private static SingletonHungry instance = new SingletonHungry();
private SingletonHungry(){}
/**
* 單例物件的方法
*/
public void process() {
System.out.println("方法實體化成功!");
}
public static SingletonHungry getInstance(){
return instance;// 當類加載的時候就直接實體化物件
}
}
單例模式——餓漢式呼叫:
@Test
public void testSingletonHungry(){
SingletonHungry.getInstance().process();
}
餓漢式單例模式,當類加載的時候就直接實體化物件,因此不需要考慮執行緒安全問題,
- 優點:實作簡單,不需要考慮執行緒安全問題,
- 缺點:不管有沒有使用該物件實體,instance物件一直占用著這段記憶體,
懶漢與餓漢式如何選擇?
- 如果物件記憶體占用不大,且創建不復雜,直接使用餓漢的方式即可,
- 其他情況均采用懶漢方式(優選),
總結的面試題也挺費時間的,文章會不定時更新,有時候一天多更新幾篇,如果幫助您復習鞏固了知識點,還請三連支持一下,后續會億點點的更新!

為了幫助更多小白從零進階 Java 工程師,從CSDN官方那邊搞來了一套 《Java 工程師學習成長知識圖譜》,尺寸 870mm x 560mm,展開后有一張辦公桌大小,也可以折疊成一本書的尺寸,有興趣的小伙伴可以了解一下,當然,不管怎樣博主的文章一直都是免費的~

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/286273.html
標籤:java
