![](/img/trans.png)
[英]Singleton with Enum vs Singleton with double-checked locking
[英]Lazy-loaded singleton: Double-checked locking vs Initialization on demand holder idiom
我需要在並發環境中延遲加載資源。 加載資源的代碼應該只執行一次。
雙重檢查鎖定(使用 JRE 5+ 和 volatile 關鍵字)和按需初始化持有者習語似乎都適合這項工作。
只看代碼,Initialization on demand holder idiom 似乎更干凈、更高效(但是,嘿,我猜這里)。 盡管如此,我還是必須注意並記錄每個單身人士的模式。 至少對我來說,很難理解為什么當場寫成這樣的代碼......
我的問題是:哪種方法更好? 為什么? 如果你的答案是否定的。 您將如何在 Java SE 環境中解決此要求?
備擇方案
我可以為此使用 CDI 而不強制它在我的整個項目中使用嗎? 有文章出來嗎?
添加另一個可能更清潔的選項。 我建議枚舉變體:
就可讀性而言,我會使用按需初始化的 go 持有人。 我覺得雙重檢查鎖定是一個過時且丑陋的實現。
從技術上講,通過選擇雙重檢查鎖定,您總是會在字段上產生易失性讀取,因為您可以使用按需初始化持有者習語進行正常讀取。
按需初始化持有人僅適用於 singleton,您不能擁有每個實例延遲加載的元素。 雙重檢查鎖定給必須查看 class 的每個人帶來認知負擔,因為很容易以微妙的方式出錯。 在我們將模式封裝到並發庫中的實用程序 class 之前,我們曾經遇到過各種各樣的麻煩
我們有以下選擇:
Supplier<ExpensiveThing> t1 = new LazyReference<ExpensiveThing>() {
protected ExpensiveThing create() {
… // expensive initialisation
}
};
Supplier<ExpensiveThing> t2 = Lazy.supplier(new Supplier<ExpensiveThing>() {
public ExpensiveThing get() {
… // expensive initialisation
}
});
就用法而言,兩者具有相同的語義。 第二種形式使內部供應商使用的任何引用在初始化后都可用於 GC。 第二種形式還支持使用 TTL/TTI 策略的超時。
按需初始化持有者始終是實現 singleton 模式的最佳實踐。 它很好地利用了 JVM 的以下特性。
此外,您不必使用 synchronize 關鍵字,它會使您的程序慢 100 倍。
我懷疑按需初始化持有人比雙重檢查鎖定(使用易失性)稍微快一些。 原因是前者在創建實例后沒有同步開銷,但后者涉及讀取 volatile (我認為)需要完整的 memory 讀取。
如果性能不是一個重要的問題,那么同步的getInstance()
方法是最簡單的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.