1. 程式人生 > >Jva單例模式的幾種坑

Jva單例模式的幾種坑

Java單例模式的幾種坑

是什麼

在一個JVM程序中,一個類對應的例項物件有且只有一個。

為什麼

因為在一個程式中,有些業務邏輯和流程是重複的、通用的,沒有必要在每次執行時再進行new相同物件的操作。

優點

只進行一次new操作,沒有物件的頻繁建立和回收,提高了JVM的執行響應速度。尤其是在高併發的情況下,對程式的執行有很大的提升。

1、在多執行緒的場景中,如果單例物件是通用類還好,如果單例物件中有鎖的情況,是非常影響程式的執行速率;
2、單例物件在建立時,不能傳遞引數,否則就不算是單例物件了。

建立步驟

1、一個私有的、全域性的、靜態的單例物件引用;
2、一個私有建構函式;
3、一個獲取該引用的方法。

分類

基於類物件的建立時間不同,分為餓漢模式和懶漢模式兩種


餓漢模式
–不管用不用,先載入了再說。

class Singleton {
    private static Singleton instance = new Singleton();
    private Singleton() {
    }
    public static Singleton getInstace() {
        return instance;
    }
}

優點
1、執行快:不管程式在以後會不會用到,在類載入的過程中,就把這個單例物件建立好,如果在後續的執行中有用到,呼叫getInstace()就直接獲取到這個單例物件了;
2、執行緒安全

1、程式啟動慢,因為這個物件在程式啟動時就建立了;
2、程式佔用記憶體較多,如果程式中有多個這種方式編寫的不同單例物件,堆中會有這些單例物件,但這些單例物件,有可能是在程式執行到某個分支才用到。


懶漢模式 (不推薦下面這個寫法)
–什麼時候用,什麼時候再載入,即延遲載入。

class Singleton {
    private static Singleton instance;
private Singleton() { } public static Singleton getInstace() { if (instance == null) { instance = new Singleton(); } return instance; } }

優點
只有在第一次被用到的時候才建立。

在多執行緒情況下併發呼叫getInstace(),會出現建立了多個例項,不能保證這個物件是單
例。

改進1(不推薦下面這個寫法)

class Singleton {
    private static Singleton instance;
    private Singleton() {
    }
    public static synchronized Singleton getInstace() {
    	if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}


1、在多執行緒場景下,雖然能保證物件的單例性,卻降低了程式的執行響應速度,因為每個要獲取這個單例物件的執行緒都需要進行的鎖的等待和獲取;
2、在執行instance = new Singleton();這句話時,CPU實際上執行的是多條指令,也就會出現指令重排的情況,這樣有可能出現執行緒1只給instance引用賦了值,但值還沒有完全建立好,此時,執行緒2來了判斷引用不是null就以後獲取到這個單例物件的情況。

改進2

class Singleton {
    private volatile static Singleton instance;
    private Singleton() {
    }
    public static synchronized Singleton getInstace() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

這樣修改後,用雙重檢查機制來保證在多執行緒的情況下,可以正常高效執行;
1、只有在instance為null時才獲取鎖,減小了多執行緒要獲取鎖的情況;
2、通過volatile關鍵字,來防止CPU進行指令重排,保證這個關鍵字對應的內容能順序執行。

改進3 (推薦)

class Singleton {
    //private volatile static Singleton instance;
    private static class LazyHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
	private Singleton() {
    }
    public static synchronized Singleton getInstace() {
        return LazyHolder.INSTANCE;;
    }
}

通過內部類的方式,來實現多執行緒下的安全。


總結:
從速度和響應時間上看,餓漢模式比較好;
從程式記憶體的使用上看,懶漢模式比較好。