聊聊如何實現(xiàn)更優(yōu)雅的單例?
閱讀本文大概需要 5 分鐘。
來自:https://blog.csdn.net/weixin_43178828
前言
從最簡單的開始 getInstance
你訪問這個實例對象的時候 這個實例真的已經(jīng)創(chuàng)建出來了嗎? 這個實例對象是怎么創(chuàng)建出來的?創(chuàng)建的機(jī)制其實各有不同 比如所謂懶漢式的創(chuàng)建 是需要你 依賴他這個實例的時候 才會觸發(fā) 從而創(chuàng)建單例的 為的是節(jié)省內(nèi)存空間——沒人用我創(chuàng)建他出來干嘛? 所以 在你獲取對象之前 是不是有個觸發(fā)的東西呢?說白了前邊還有些代碼邏輯實現(xiàn)懶漢式的思路 如果能夠直接訪問到這個實例對象 怎么保證是單例呢?我上次訪問的和這次 是同一個對象? 如何保證創(chuàng)建的是單例 尤其是多線程的環(huán)境下
public class MySingleton {
private static MySingleton instance = new MySingleton();
private Singleton() {
}
public static MySingleton getInstance() {
return instance;
}
}
MySingleton singleton = MySingleton.getInstance();復(fù)雜單例的創(chuàng)建過程——用串行化的static代碼塊解決
public class MySingleton {
private static MySingleton instance = null;
private static OtherSingleton helper = null;
static{
helper = OtherSingleton.getInstance();
instance = MySingleton(helper);
}
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static MySingleton getInstance() {
return instance;
}
}
OtherSingleton 的getInstance負(fù)責(zé) 我們這里最多加一層檢查 攔截拋異常 ) 不會出現(xiàn) 因為多線程 導(dǎo)致創(chuàng)建的時候 helper拿不到的情況。。getInstance里邊也可以添加獨(dú)特的東西(懶漢式我們后邊再聊)懶漢式(延遲創(chuàng)建)
getInstance時如果沒有創(chuàng)建 則 開始創(chuàng)建 并返回單例 已經(jīng)創(chuàng)建 則直接返回
public class MySingleton {
private static MySingleton instance = null;
private static OtherSingleton helper = null;
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static MySingleton getInstance() {
if(instance == null)
instance = new MySingleton(OtherSingleton.getInstance());
return instance;
}
}
instance == null 當(dāng)然進(jìn)的來)內(nèi)存泄漏?
解決方案:
synchronized 讓整個代碼塊順序執(zhí)行 就類似static代碼塊一樣:public class MySingleton {
private static MySingleton instance = null;
private static OtherSingleton helper = null;
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static synchronized MySingleton getInstance() {
if(instance == null)
instance = new MySingleton(OtherSingleton.getInstance());
return instance;
}
}
更高的性能 double check locking
public class MySingleton {
private static MySingleton instance = null;
private static OtherSingleton helper = null;
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static synchronized MySingleton getInstance() {
if(instance == null){
synchronized (MySingleton.class){
if(instance == null){
instance = new MySingleton(OtherSingleton.getInstance());
}
}
}
return instance;
}
}
MySingleton.class對象 作為臨界 意圖明顯 class對象唯一 所以我們鎖類 這樣就保證了單線程的創(chuàng)建實例 其實和static靜態(tài)塊異曲同工 因為static也是類初始化的時候執(zhí)行的 同樣也是保證串行 綁定了class對象的執(zhí)行可見性 volatile
instance!多線程在下一個指令周期 搶到了CPU計算的時間片 執(zhí)行 那個時候緩存默認(rèn)是不更新的volatile 他自然是個native的關(guān)鍵字 底層依賴C來實現(xiàn)變量的可見性!所以我們終極的程序應(yīng)當(dāng)是:public class MySingleton {
private volatile static MySingleton instance = null;
private static OtherSingleton helper = null;
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static synchronized MySingleton getInstance() {
if(instance == null){
synchronized (MySingleton.class){
if(instance == null){
instance = new MySingleton(OtherSingleton.getInstance());
}
}
}
return instance;
}
}
另一種懶漢式創(chuàng)建單例——靜態(tài)內(nèi)部類
static來創(chuàng)建了 但是可不可能用另外一個類的static來保證懶漢式單例呢?public class MySingleton {
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
public static class MySingletonAdapter {
private static final MySingleton instance = new MySingleton(OtherSingleton.getInstance());
}
public static getInstance() {
return MySingletonAdapter.instance;
}
}
Initialization on demand holder序列化單例
Serializable接口的單例 序列化倒沒什么問題 但是反序列化時會產(chǎn)生新的實例對象 這里我們得改改readResolve()方法 使得返回的實例保證單例public class MySingleton {
private static final long serialVersionUID = -3453453414141241L;
private static MySingleton instance = new MySingleton(OtherSingleton.getInstance());
private MySingleton(OtherSingleton helper) {
if(helper) this.helper = helper;
else throw new MyException("OtherSingleton getInstance failed");
}
private Object readResolve() {
return instance;
}
}
后記
OtherSingleton.getInstance() 但是有沒有想過 系統(tǒng)剛開始一啟動 實例化 誰先誰后呢?假設(shè)Other是后邊才實例化的 前邊的MySingleton的創(chuàng)建不是吃癟了嘛???OtherSingleton初始化是需要MySingleton的 怎么辦呢?如果幾百個bean 初始化的時候互相依賴 該怎么解決?invertion of controll) 控制翻轉(zhuǎn)思想 其實際實現(xiàn)是通過依賴注入dependency injection。互聯(lián)網(wǎng)初中高級大廠面試題(9個G) 內(nèi)容包含Java基礎(chǔ)、JavaWeb、MySQL性能優(yōu)化、JVM、鎖、百萬并發(fā)、消息隊列、高性能緩存、反射、Spring全家桶原理、微服務(wù)、Zookeeper......等技術(shù)棧! ?戳閱讀原文領(lǐng)取! 朕已閱
評論
圖片
表情


