1. 程式人生 > >JAVA單例模式實現詳解

JAVA單例模式實現詳解

單例模式的特點

  • 類的內部包括待返回的類的例項,為private static型別
  • 類的建構函式為私有建構函式,以防止在其他類中例項化,private的作用是防止在其他類中用建構函式建立該類的例項
  • 提供一個獲取例項的靜態方法

單例模式1:飽漢模式+考慮了執行緒安全(雙重檢查鎖)

public class Singleton1 {
    private static volatile Singleton1 instance = null;//volatile不具有原子性

    //構造方法:如果構造方法是 public,那麼任何類都可以 new 我的物件,不能保證單例,
// 所以單例模式構造方法一定是 private的 private Singleton1() { } public static Singleton1 getInstance() { if (instance == null) {//只有instance == null這種情況下需要進行加鎖synchronized synchronized (Singleton1.class) {//靜態方法中上鎖用的是類名.class if (instance == null) instance = new
Singleton1(); } } return instance; } }

為什麼需要第一重instance == null呢?

這裡就涉及一個性能問題了,因為對於單例模式的話,newSingleTon()只需要執行一次就 OK 了,

而如果沒有第一重instance == null 的話,每一次有執行緒進入getInstance()時,均會執行鎖定操作來實現執行緒同步,

這是非常耗費效能的,而如果我加上第一重instance == null 的話,

那麼就只有在第一次,也就是instance ==null 成立時的情況下執行一次鎖定以實現執行緒同步,

而以後的話,便只要直接返回Singleton例項就 OK 了而根本無需再進入 lock語句塊了,這樣就可以解決由執行緒同步帶來的效能問題了。

為什麼需要第二重instance == null呢?

考慮這樣一種情況,就是有兩個執行緒同時到達,即同時呼叫getInstance() 方法,

此時由於instance == null ,所以很明顯,兩個執行緒都可以通過第一重的 instance == null ,

進入第一重 if語句後,由於存在鎖機制,所以會有一個執行緒進入 lock 語句並進入第二重 instance == null ,

而另外的一個執行緒則會在lock 語句的外面等待。

而當第一個執行緒執行完new SingleTon()語句後,便會退出鎖定區域,此時,第二個執行緒便可以進入lock 語句塊,

此時,如果沒有第二重instance == null 的話,那麼第二個執行緒還是可以呼叫 new SingleTon()語句,

這樣第二個執行緒也會建立一個SingleTon例項,這樣也還是違背了單例模式的初衷的,

所以這裡必須要使用雙重檢查鎖定。

為什麼要用volatile?

主要是為了保證有序性(禁止指令重排序)

java中new一個物件是由多個步驟組成的,而這些步驟可能會被編譯器重排序,從而導致其他執行緒獲取到一個沒有被完全初始化的物件,而 volatile 就
是告訴編譯器不要重排序。

這裡volatile的作用僅僅是阻止指令重排序, 不涉及可見性問題, 可見性已經由synchronized來保證了(synchronized也有阻止重排序的功能, 但是其由monitor來實現, monitorenter和monitorexit之間的指令仍可能被重排序).

單例模式2:餓漢模式

public class Singleton2 {
    private static final Singleton2 singleton = new Singleton2();
    private Singleton2() {
    }
    public static Singleton2 getInstance() {
        return singleton;
    }
}
  • 餓漢的好處是天生的執行緒安全(得益於類載入機制),寫起來超級簡單,使用時沒有延遲;
  • 壞處是有可能造成資源浪費(如果類載入後就一直不使用單例的話)

單例模式3:Holder模式(增加了一個靜態內部類)

public class Singleton3 {
    // 載入一個類時,其內部類不會同時被載入。
    // 一個內部類被載入,當且僅當其某個靜態成員(靜態域、構造器、靜態方法等)被呼叫時發生。
    private static class SingletonHolder {
        private static final Singleton3 singleton = new Singleton3();
        private SingletonHolder() {
        }
    }
    private Singleton3() {
    }
    public static Singleton3 getInstance() {
        return SingletonHolder.singleton;
    }
}
  • 相對於餓漢模式,Holder模式僅增加了一個靜態內部類的成本,
  • 與飽漢的執行緒安全寫法的效果相當(略優),都是比較受歡迎的實現方式。同樣建議考慮。
  • 該實現方式比較簡單,而且既實現了由前述事實所保證的惰性初始化(Lazy-Initialazation),又由JVM保證了多執行緒併發訪問的正確性。

參考連結: