單例模式的各種情況簡析

2021-10-20 03:28:12 字數 1608 閱讀 6140

關鍵字:

雙重檢驗、禁止指令重排序、鎖,反射、反序列化、列舉

what

單例模式是指, 物件在記憶體中只存在乙份例項,每次獲取物件的時候,都是拿到的同乙個記憶體位址的物件。

why使用單例模式,可以避免頻繁建立、銷毀物件這樣的開銷,直接獲取到位址值就可以。 另一方面,有可能我們每次需要的都是同乙個物件,也就說有這樣的業務需求,就比如 spring 裡預設的 bean 都是 singleton 模式的。

how大體來說有 2 種方式實現。

類載入的時候就是直接進行例項初始化: 餓漢式。

比如:

public

class

singleton_hungry

public

static singleton_hungry getinstance()

}

到用到這個類的時候,才去看他有沒有進行初始化: 懶漢式。

寫最簡陋的版本,會有些問題,比如執行緒安全問題,指令重排序問題。

這裡直接給出 雙重校驗+鎖+禁止指令重排序 的版本了:

public

class

singleton_lazy

public singleton_lazy getinstance()

}}return instance;

// 這裡需要 volatile,避免發生 空指標異常。 有重排序的時間差

}}

volatile: 可以保證 可見性和有序性, 可以禁止指令重排序, 通過記憶體屏障來實現。

乙個物件的建立簡單來說可以分為 3 步:

給物件分配記憶體空間

初始化物件

將記憶體位址 指向 物件

指令重排序會打亂這些步驟,所以建立乙個物件並不是執行緒安全的。

可能先走其他執行緒現在第三步,當前執行緒判斷不為空直接返回了,實際返回的是乙個 null 。

所以需要用 volatile 禁止指令重排序。

你以為這就完了麼? 並沒有,要找漏洞也其實還有。

就是可以通過 反射 , 打破這樣的單例效果。

獲取到構造器後, setaccessible(true) 設定允許方法私有方法,就可以無限訪問 私有建構函式了。。。。

還可以通過 反序列化, 也可以 打破

readobject( ) 從檔案裡 反序列化的時候, 總是返回的乙個新的物件。

所以, 有什麼解決辦法呢?

列舉就可以~列舉天然的就是執行緒安全的, 就是單例的~

防反射、 防反序列化:

在用 反射 newinstance( ) 的時候, 會判斷當前類是不是 列舉, 如果是,就直接拋異常了

在反序列化的時候, 會用 enum 類的 valueof( ) 方法,直接根據變數名,找到對應的列舉類。

單例模式簡析

一 單例模式 單例模式確保乙個類只有乙個例項,自行提供這個例項並向整個系統提供這個例項。特點就是多讀單寫。函式簡單實現 class singleton return sinstance void printf protected singleton data 0 防拷貝,只申明不定義 singlet...

設計模式簡析(單例模式)

什麼是設計模式 設計模式是指設計某個程式的方式。今天我們簡析一下單例模式 單例模式 乙個類只能最多建立出乙個物件 實現單例必須滿足以下條件 1.不讓轉殖 私有化轉殖魔術方法 2。不讓建立物件 私有化構造方法 不能在類外部使用new關鍵字建立物件。3。進入類的內部建立物件 使用靜態方法 4.在類中使用...

設計模式之單例模式簡析筆記

好久沒這樣寫寫東西了,最近也真的是很忙,白天上班,晚上帶娃,還要學習。其實靜下心來好好的學習才發現自己要學習的太多了,技術更新換代的也很頻繁。每天堅持一點點學習,讓自己更充實,技術慢慢得到提公升。接下來的每一天都要學習,自己一定也能堅持。記錄下點滴。說的有點倉促,後面會更好 附上自己學習的一些筆記,...