所謂類的單例設計模式,就是采取一定的方法保證在整個的軟件系統(tǒng)中,對某個類只能存在一個對象實例,并且該類只提供一個取得其對象實例的方法(靜態(tài)方法)
Spring 中的 bean 默認都是單例模式,每個bean定義只生成一個對象實例,每次 getBean請求獲得的都是此實例
二 單例模式八種方式單例模式的八種實現(xiàn)方式,如下所示
class Singleton {//一:構造器的私有化 防止外部用構造器...
private Singleton() {}
//二:類的內(nèi)部創(chuàng)建對象 final static
private static final Singleton singleton = new Singleton();
//三:對外提供公共的靜態(tài)方法 返回該類唯一的對象實例
public static Singleton getInstance() {return singleton;
}
}
案例分析//案例演示 - 餓漢式
public class SingletonDemo {public static void main(String[] args) {// 方式一:靜態(tài)常量
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
System.out.println(instance == instance1); //true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
通過結果可以發(fā)現(xiàn)返回的是同一個對象所以單例模式是實現(xiàn)的。
寫法分析優(yōu)勢
: 簡單 避免多線程的同步問題劣勢
: 沒有達到懶加載的效果 內(nèi)存的浪費
//方式二:靜態(tài)代碼塊的方式
class Singleton {//構造器私有化
private Singleton() {}
//類的內(nèi)部創(chuàng)建對象
private static final Singleton singleton;
static {singleton = new Singleton();
}
//對外提供公共的靜態(tài)的方法
public static Singleton getInstance() {return singleton;
}
}
案例分析優(yōu)勢
: 簡單 避免多線程的同步問題劣勢
: 沒有達到懶加載的效果 內(nèi)存的浪費
class Singleton{//構造器私有化
private Singleton(){}
//類的內(nèi)部提供對象
private static Singleton singleton;
//對外提供公共的靜態(tài)方法的時候,來判斷
public static Singleton getInstance(){if (singleton == null){singleton = new Singleton();
}
return singleton;
}
}
案例分析優(yōu)勢
:起到了懶加載的效果 不會造成內(nèi)存浪費
在使用到的時候才會創(chuàng)建對象。判斷有無對象,有則返回,無創(chuàng)建后再返回
劣勢
:只能在單線程下使用,多線程情況下線程不安全 不推薦這種方式的
2.4 懶漢式(線程安全,同步方法)① 在多線程的情況下,有一個對象進入if判斷通過,還沒執(zhí)行到創(chuàng)建對象這一步驟時
② 有另外一個對象也進入了if判斷,也通過了。
此時就會出現(xiàn)多個實例,造成線程不安全。
在獲取對象的靜態(tài)方法上添加 synchronized 關鍵字,實現(xiàn)同步方法。解決線程不安全問題。
代碼案例//加入同步處理 同步方法
class Singleton{//構造器私有化
private Singleton(){}
//類的內(nèi)部提供對象
private static Singleton singleton;
//對外提供公共的靜態(tài)方法的時候,來判斷
public static synchronized Singleton getInstance(){if (singleton == null){singleton = new Singleton();
}
return singleton;
}
}
案例分析解決了線程安全問題,但是效率太低
2.5 懶漢式(線程不安全,同步代碼塊)每一個線程需求拿實例的時候都要在外面等候另一線程處理完
將實例化對象過程放入同步代碼塊中
代碼案例//加入同步處理 - 同步代碼塊的方式 不推薦的
class Singleton{//構造器私有化
private Singleton(){}
//類的內(nèi)部提供對象
private static Singleton singleton;
//對外提供公共的靜態(tài)方法的時候,來判斷
public static Singleton getInstance(){if (singleton == null){synchronized (Singleton.class){singleton = new Singleton();
}
}
return singleton;
}
}
案例分析不推薦的,解決不了線程的安全問題
2.6 雙重檢查 (推薦使用) 代碼案例這種方式本意是想解決同步方法的問題。但是我們分析一下。在多線程情況下還是有可能創(chuàng)建多個實例的問題。
class Singleton{private Singleton(){}
//禁止指令重排
private static volatile Singleton singleton;
//加入雙重檢查機制
public static Singleton getInstance(){if (singleton == null){synchronized (Singleton.class){if (singleton == null){singleton = new Singleton();
}
}
}
return singleton;
}
}
案例分析
優(yōu)點線程安全
解決了線程安全問題
① 多線程時,若兩個線程同時滿足第一層非空判斷,等待調(diào)用同步代碼塊。
② 由于添加了synchronized ,當?shù)谝粋€線程進來時,發(fā)現(xiàn)Singleton對象為空,進行創(chuàng)建對象并返回。
③ 當另外一個線程調(diào)用同步代碼塊時,進行再次對象非空判斷,發(fā)現(xiàn)對象已經(jīng)創(chuàng)建成功。不在執(zhí)行創(chuàng)建對象流程,返回已有對象
懶加載
使用到對象時才創(chuàng)建,不會造成內(nèi)存的浪費
效率很高
可能出現(xiàn)的問題先進性了singleton 是否為空的判斷,singleton 如果不為空直接返回結果。倘若無第一層判空,多線程時每次都要進行synchronized 等待其他線程處理結束,才能進入內(nèi)部非空判斷,效率相對低。
我們認為的 new Singleton() 操作
1)分配內(nèi)存地址 M
2)在內(nèi)存 M 上初始化Singleton 對象
3)將M的地址賦值給 instance 對象
JVM編譯優(yōu)化后(指令重排)可能的 new Singleton() 操作
1)分配內(nèi)存地址 M
2)將M的地址賦值給instance變量
3)在內(nèi)存M上初始化 Singleton 對象
這就有可能出現(xiàn)空指針異常
異常發(fā)生過程
解決方式:關鍵字 Volatile 來禁止指令重排
輕量級的同步機制 (低配版) 沒有保證原子性
三大特性
保證可見性
其中一個線程修改了主內(nèi)存共享變量的值,要寫回主內(nèi)存,并要及時通知其他線程可見
沒有保證原子性
沒法(不能保證)不可分割,完整,要么同時成功,要么同時失敗
禁止指令重排
和底層內(nèi)存屏障相關 避免多線程下出現(xiàn)指令亂序的情況
擴展-線程切換
Java的一條語句對應的cpu指令可能是多條,其中任意一條cpu指令在執(zhí)行完都可能發(fā)生線程切換
count += 1,對應cpu 指令如下:
2.7 靜態(tài)內(nèi)部類 (推薦使用) 代碼案例1)將變量count從內(nèi)存加載到cpu寄存器
2)寄存器中 +1
3)將結果寫入內(nèi)存(緩存機制寫入的可能是cpu而不是內(nèi)存)
class Singleton{private Singleton(){}
private static class SingletonInstance{public static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance(){return SingletonInstance.INSTANCE;
}
}
案例分析①當Singleton類加載時,靜態(tài)內(nèi)部類是不會加載的。
②只有調(diào)用getInstance方法用到了SingletonInstance.INSTANCE靜態(tài)變量時導致SingletonInstance靜態(tài)內(nèi)部類進行加載。
不會出現(xiàn)線程安全問題
JVM來幫我們保證了線程的安全性
利用靜態(tài)內(nèi)部類的特點,效率也很高,實際開發(fā)中推薦使用的
public class EnumDemo {public static void main(String[] args) {//驗證其正確性
Singleton instance = Singleton.INSTANCE;
Singleton instance1 = Singleton.INSTANCE;
System.out.println(instance == instance1); //true
System.out.println(instance.hashCode());
System.out.println(instance1.hashCode());
}
}
enum Singleton{INSTANCE; //屬性
}
案例分析不僅可以避免線程安全問題 還可以防止反序列化重新創(chuàng)建對象。推薦使用
三 注意事項你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調(diào)度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧