作者:Yujiaao
https://segmentfault.com/a/1190000019962661
一個很艱難的 Java 核心面試問題,這個 Java 問題也常被問: 什么是執行緒安全的單例,你怎么創建它,
好吧,在Java 5之前的版本, 使用雙重檢查鎖定創建單例 Singleton 時,如果多個執行緒試圖同時創建Singleton實體,則可能有多個 Singleton實體被創建,
從 Java 5 開始,使用 Enum 創建執行緒安全的Singleton`很容易,但如果面試官堅持雙重檢查鎖定,那么你必須為他們撰寫代碼,記得使用volatile變數,
為什么列舉單例在 Java 中更好
列舉單例是使用一個實體在 Java 中實作單例模式的新方法,雖然Java中的單例模式存在很長時間,但列舉單例是相對較新的概念,在引入Enum作為關鍵字和功能之后,從Java5開始在實踐中,
本文與之前關于 Singleton 的內容有些相關, 其中討論了有關 Singleton 模式的面試中的常見問題, 以及 10 個 Java 列舉示例, 其中我們看到了如何通用列舉可以,
這篇文章是關于為什么我們應該使用Eeame作為Java中的單例,它比傳統的單例方法相比有什么好處等等,
Java 列舉和單例模式
Java 中的列舉單例模式是使用列舉在 Java 中實作單例模式,單例模式在 Java 中早有應用, 但使用列舉型別創建單例模式時間卻不長. 如果感興趣, 你可以了解下構建者設計模式和裝飾器設計模式,
1)列舉單例易于書寫
這是迄今為止最大的優勢,如果你在Java 5之前一直在撰寫單例, 你知道, 即使雙檢查鎖定, 你仍可以有多個實體,
雖然這個問題通過 Java 記憶體模型的改進已經解決了, 從 Java 5 開始的 volatile 型別變數提供了保證, 但是對于許多初學者來說, 撰寫起來仍然很棘手,
與同步雙檢查鎖定相比,列舉單例實在是太簡單了,如果你不相信, 那就比較一下下面的傳統雙檢查鎖定單例和列舉單例的代碼:
在 Java 中使用列舉的單例
這是我們通常宣告列舉的單例的方式,它可能包含實體變數和實體方法,但為了簡單起見,我沒有使用任何實體方法,只是要注意,如果你使用的實體方法且該方法能改變物件的狀態的話, 則需要確保該方法的執行緒安全,
默認情況下,創建列舉實體是執行緒安全的,但 Enum 上的任何其他方法是否執行緒安全都是程式員的責任,
/**
* 使用 Java 列舉的單例模式示例
*/
public enum EasySingleton{
INSTANCE;
}
你可以通過EasySingleton.INSTANCE來處理它,這比在單例上呼叫getInstance()方法容易得多,
具有雙檢查鎖定的單例示例
下面的代碼是單例模式中雙重檢查鎖定的示例,此處的 getInstance() 方法檢查兩次,以查看 INSTANCE 是否為空,這就是為什么它被稱為雙檢查鎖定模式,請記住,雙檢查鎖定是代理之前Java 5,但Java 5記憶體模型中易失變數的干擾,它應該作業完美,
/**
* 單例模式示例,雙重鎖定檢查
*/
public class DoubleCheckedLockingSingleton{
private volatile DoubleCheckedLockingSingleton INSTANCE;
private DoubleCheckedLockingSingleton(){}
public DoubleCheckedLockingSingleton getInstance(){
if(INSTANCE == null){
synchronized(DoubleCheckedLockingSingleton.class){
//double checking Singleton instance
if(INSTANCE == null){
INSTANCE = new DoubleCheckedLockingSingleton();
}
}
}
return INSTANCE;
}
}
你可以呼叫DoubleCheckedLockingSingleton.getInstance() 來獲取此單例類的訪問權限,
現在,只需查看創建延遲加載的執行緒安全的 Singleton 所需的代碼量,使用列舉單例模式, 你可以在一行中具有該模式, 因為創建列舉實體是執行緒安全的, 并且由 JVM 進行,
人們可能會爭辯說,有更好的方法來撰寫 Singleton 而不是雙檢查鎖定方法, 但每種方法都有自己的優點和缺點, 就像我最喜歡在類加載時創建的靜態欄位 Singleton, 如下面所示, 但請記住, 這不是一個延遲加載單例:
單例模式用靜態工廠方法
這是我最喜歡的在 Java 中影響 Singleton 模式的方法之一,因為 Singleton 實體是靜態的,并且最后一個變數在類首次加載到記憶體時初始化,因此實體的創建本質上是執行緒安全的,
/**
* 單例模式示例與靜態工廠方法
*/
public class Singleton{
//initailzed during class loading
private static final Singleton INSTANCE = new Singleton();
//to prevent creating another instance of Singleton
private Singleton(){}
public static Singleton getSingleton(){
return INSTANCE;
}
}
你可以呼叫 Singleton.getSingleton() 來獲取此類的訪問權限,
**2) 列舉單例自行處理序列化
**傳統單例的另一個問題是,一旦實作可序列化介面,它們就不再是 Singleton, 因為 readObject() 方法總是回傳一個新實體, 就像 Java 中的建構式一樣,通過使用 readResolve() 方法, 通過在以下示例中替換 Singeton 來避免這種情況:
//readResolve to prevent another instance of Singleton
private Object readResolve(){
return INSTANCE;
}
如果 Singleton 類保持內部狀態, 這將變得更加復雜, 因為你需要標記為 transient(不被序列化),但使用列舉單例, 序列化由 JVM 進行,
3) 創建列舉實體是執行緒安全的
如第 1 點所述,因為 Enum 實體的創建在默認情況下是執行緒安全的, 你無需擔心是否要做雙重檢查鎖定,關注微信公眾號_Java技術堆疊_在后臺回復_Java_可以獲取我整理的Java 多執行緒干貨,
總之, 在保證序列化和執行緒安全的情況下,使用兩行代碼列舉單例模式是在 Java 5 以后的世界中創建 Singleton 的最佳方式,你仍然可以使用其他流行的方法, 如你覺得更好, 歡迎討論,
推薦去我的博客閱讀更多:
1.Java JVM、集合、多執行緒、新特性系列教程
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
4.Java、后端、架構、阿里巴巴等大廠最新面試題
覺得不錯,別忘了點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/186829.html
標籤:Java
