區塊鏈V1版本實現之五
單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。
這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要例項化該類的物件。
注意:
- 1、單例類只能有一個例項。
- 2、單例類必須自己建立自己的唯一例項。
- 3、單例類必須給所有其他物件提供這一例項。
下面介紹實現單例的兩種方式:懶漢式、餓漢式。
/** * 懶漢式單例模式 * 懶載入,必須加鎖 synchronized 才能保證單例 */ public class Singleton { public static Singleton instance; private Singleton() { } public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
/**
* 餓漢式單例模式
* 沒有加鎖,執行效率會提高。
* 類載入時建立物件,消耗記憶體
*/
public class Singleton {
public static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
兩種方式都是將構造器 private,防止外界通過 new 來建立例項。
並通過 public static 返回一個例項。
單例模式下如何保證執行緒安全!
通常情況下,一個單例模式是不會出問題的,但是如果在多執行緒的情況下,有可能會生成多個物件。
public class Singleton {
private Singleton() {
}
private static final Singleton instance = null;
public Singleton getInstance() {
return new Singleton();
}
}
如果在多執行緒的情況下,instance 是一個共享資源,如果不進行任何保護機制的話,兩個執行緒有可能同時進入臨界區,這樣這個系統會有超過一個的 Singleton 物件,單例也就失效了。想要保證在多執行緒下依然有用的話,就需要設定鎖機制。
單例模式,執行緒安全的兩種實現
一、雙重校驗鎖( double-checked locking )
/**
* 雙重校驗鎖
*/
public class Singleton {
/* 保證 instance 在所有執行緒中同步 */
public static volatile Singleton instance;
/* private 避免類在外部被例項化 */
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
分析
-
第一次判斷 instance 是否為null
第一次判斷實在 Synchronized 同步程式碼塊前面判斷的,由於單例模式只會建立一個例項,並通過 getInstance() 方法返回物件。所以第一次判斷的原因是為了在 Singleton 物件已經被建立的情況下,避免進入同步程式碼塊,提升效率。
-
第二次判斷 instance 是否為null
1、假設:執行緒A已經經過第一次判斷,判斷 instance 為null,並準備進入 synchronized 程式碼塊。
2、此時執行緒B獲取時間片,由於執行緒A還沒有建立例項,所以 instance == null,這時候執行緒B建立了例項。
3、此時執行緒A再次獲取時間片,由於剛才經過 instance == null,進入同步程式碼塊。這個時候如果不再次進行 判斷,執行緒A又會再次建立一個例項。這樣就不滿足單例模式的要求,所以第二次判斷很有必要!
二、靜態內部類
/**
* 靜態內部類單例
*/
public class Singleton {
private Singleton() {
}
public static class SingletonHolder {
private static final Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.singleton;
}
}
Java中靜態內部類可以訪問其外部類的靜態成員屬性,同時,靜態內部類只有當被呼叫的時候才開始首次被載入,利用了classloader的機制來保證初始化instance時只有一個執行緒,所以也是執行緒安全的,同時沒有效能損耗(加 synchronized 同步鎖),這種實現更推薦。