Java實現(xiàn)線程安全單例模式的五種方式的示例代碼
餓漢式
餓漢式:類加載就會導致該單實例對象被創(chuàng)建
// 問題1:為什么加 final // 問題2:如果實現(xiàn)了序列化接口, 還要做什么來防止反序列化破壞單例 public final class Singleton_hungry implements Serializable { // 問題3:為什么設置為私有? 是否能防止反射創(chuàng)建新的實例? private Singleton_hungry(){} // 問題4:這樣初始化是否能保證單例對象創(chuàng)建時的線程安全? private static Singleton_hungry INSTANCE = new Singleton_hungry(); // 問題5:為什么提供靜態(tài)方法而不是直接將 INSTANCE 設置為 public, 說出你知道的理由 public static Singleton_hungry getInstance() { return INSTANCE; } public Object readResolve(){ // 防止反射創(chuàng)建新的實例? return INSTANCE; } }
- 問題1:避免子類覆蓋父類的一些方法,導致線程不安全。
- 問題2:實現(xiàn)
readResolve
方法。當從對象流ObjectInputStream
中讀取對象時,會檢查對象的類否定義了readResolve
方法。如果定義了,則調(diào)用它返回我們想指定的對象(這里就指定了返回單例對象)。 - 問題3:防止通過
new
創(chuàng)建對象實例。不能防止反射創(chuàng)建新的實例。 - 問題4:可以。靜態(tài)變量初始化在類加載時進行,由
jvm
進行管理,可以保證線程安全。 - 問題5:通過方法,可以提高拓展性,改進餓漢式轉(zhuǎn)化為懶漢式、利用泛型特性、增加對單例對象的控制操作。
枚舉單例
enum Singleton { INSTANCE; }
問題1:枚舉單例是如何限制實例個數(shù)的
單例相當于枚舉的靜態(tài)成員變量,定義幾個就有幾個實例。
問題2:枚舉單例在創(chuàng)建時是否有并發(fā)問題
單例相當于枚舉的靜態(tài)成員變量,類加載時初始化,由 jvm
進行管理,可以保證線程安全。
問題3:枚舉單例能否被反射破壞單例
不能
問題4:枚舉單例能否被反序列化破壞單例
枚舉實現(xiàn)了 Serializable
接口,可序列化,但不會被反序列破壞單例。
問題5:枚舉單例屬于懶漢式還是餓漢式
餓漢式
問題6:枚舉單例如果希望加入一些單例創(chuàng)建時的初始化邏輯該如何做
枚舉允許構(gòu)造方法
懶漢式
public final class Singleton_lazy { private Singleton_lazy(){} private static Singleton_lazy INSTANCE = null; // 缺點 public static synchronized Singleton_lazy getInstance() { if(INSTANCE != null) { return INSTANCE; } INSTANCE = new Singleton_lazy(); return INSTANCE; } }
synchronized
保證線程安全,但鎖粒度較大,性能低。
DCL 懶漢式
public final class Singleton_DCL { private Singleton_DCL() {} // 問題1:解釋為什么要加 volatile ? private static volatile Singleton_DCL INSTANCE= null; // 問題2:對比實現(xiàn)3, 說出這樣做的意義 public static Singleton_DCL getInstance() { if(INSTANCE != null) { return INSTANCE; } synchronized (Singleton_DCL.class) { // 問題3:為什么還要在這里加為空判斷, 之前不是判斷過了嗎 if(INSTANCE != null) { return INSTANCE; } INSTANCE = new Singleton_DCL(); return INSTANCE; } } }
問題1:避免指令重排序,導致賦值語句先于構(gòu)造函數(shù)執(zhí)行,得到一個未初始化完畢的對象。
問題2、3:Double Check Lock
機制。同步代碼塊外部的判斷語句主要用于 INSTANCE
初始化并賦值之后,此時 INSTANCE != null
,如果有多個線程嘗試獲取單例,可以提前返回,不用執(zhí)行同步代碼塊。而同步代碼塊內(nèi)部的判斷主要用于第一次初始化時,INSTANCE = null
,此時可以有多個線程嘗試獲取 INSTANCE
,只能有一個線程進入同步代碼塊,其他線程在同步代碼塊外阻塞,該線程創(chuàng)建一個單例對象之后,喚醒其他線程,再進入同步代碼塊,發(fā)現(xiàn) INSTANCE != null
,則直接返回,不用重新創(chuàng)建單例對象,提高了效率。
靜態(tài)內(nèi)部類懶漢單例
public final class Singleton_LazyHolder { private Singleton_LazyHolder(){} // 問題1:屬于懶漢式還是餓漢式 private static class LazyHolder{ static final Singleton_LazyHolder INSTANCE = new Singleton_LazyHolder(); } // 問題2:在創(chuàng)建時是否有并發(fā)問題 public static Singleton_LazyHolder getInstance() { return LazyHolder.INSTANCE; } }
問題1:懶漢式。靜態(tài)內(nèi)部類只有在被方法調(diào)用的時候才進行初始化,類加載。
問題2:無,類加載由 jvm
進行,線程安全。
到此這篇關于Java實現(xiàn)線程安全單例模式的五種方式的示例代碼的文章就介紹到這了,更多相關Java單例模式內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
javaweb中mysql數(shù)據(jù)庫連接步驟方法及其實例
這篇文章主要介紹了使用java web 連接MySQL數(shù)據(jù)庫的驅(qū)動方法的相關知識,本文介紹的非常詳細,具有參考借鑒價值,需要的朋友可以參考下2017-04-04Mybatis-Plus自動生成的數(shù)據(jù)庫id過長的解決
這篇文章主要介紹了Mybatis-Plus自動生成的數(shù)據(jù)庫id過長的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12