單例模式的概念
單例模式就是確保只有一個實例,而且自行實例化并向整個系統傳遞這個實例,這個類就稱作為單例類
單例模式最重要的一個特點就是構造方法私有化。創建單例模式分為懶漢式和餓漢式。
第一種:懶漢式(線程不安全的)
傳統的懶漢式創建單例模式,是線程不安全的
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
所謂懶漢式單例模式就是在調用的時候才去創建這個實例。這種寫法的懶加載很明顯,但是缺點就是不能再多線程訪問下正常工作。
第二種:懶漢式(線程安全的)
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
線程安全的方式創建單例就是在對外的創建實例方法上加上synchronized。 這種寫法能夠在多線程中很好的工作,而且看起來它也具備很好的lazy loading,但是,遺憾的是,效率很低,99%情況下不需要同步。
第三種:餓漢式(是線程安全的)
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
這種方式基于classloder機制避免了多線程的同步問題,不過,instance在類裝載時就實例化,雖然導致類裝載的原因有很多種,在單例模式中大多數都是調用getInstance方法, 但是也不能確定有其他的方式(或者其他的靜態方法)導致類裝載,這時候初始化instance顯然沒有達到lazy loading的效果。
第四種:靜態內部類的方式創建單例模式(static inner class)
public class Singleton {
private Singleton() {
}
private static class SingletonHolder {// 靜態內部類
private static Singleton singleton = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.singleton;
}
}
這種方式同樣利用了classloder的機制來保證初始化instance時只有一個線程,它跟第三種方式不同的是(很細微的差別):第三種方式是只要Singleton類被裝載了,那么instance就會被實例化(沒有達到lazy loading效果),而這種方式是Singleton類被裝載了,instance不一定被初始化。因為SingletonHolder類沒有被主動使用,只有顯示通過調用getInstance方法時,才會顯示裝載SingletonHolder類,從而實例化singleton。想象一下,如果實例化singleton很消耗資源,我想讓他延遲加載,另外一方面,我不希望在Singleton類加載時就實例化,因為我不能確保Singleton類還可能在其他的地方被主動使用從而被加載,那么這個時候實例化instance顯然是不合適的。這個時候,這種方式相比第三種方式就顯得很合理。
第五種:雙重校驗鎖(dubble check instance)
public class Singleton {
private static Singleton singleton;
private Singleton() {
}
public static Singleton getInstance(){
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
/**
* 為什么這里會使用雙重判定呢?
*/
singleton = new Singleton();
}
}
}
return singleton;
}
}
這種事用雙重判斷來創建一個單例的方法,那么我們為什么要使用兩個if判斷這個對象當前是不是空的呢 ?因為當有多個線程同時要創建對象的時候,多個線程有可能都停止在第一個if判斷的地方,等待鎖的釋放,然后多個線程就都創建了對象,這樣就不是單例模式了,所以我們要用兩個if來進行這個對象是否存在的判斷。