博文原址:折騰Java設計模式之單例模式html
Ensure a class has only one instance, and provide a global point of access to it.java
一個類僅僅只有一個實例,而且提供全局的接入點。簡潔點理解就是涉及到一個單一的類,該類負責建立本身的對象,同時確保只有單個對象被建立。這個類提供了一種訪問它本身惟一的對象的方式,能夠直接訪問,不須要實例化該類的對象。git
public final class EagerSingleton { private static final EagerSingleton INSTANCE = new EagerSingleton(); private EagerSingleton() { } public static EagerSingleton getInstance() { return INSTANCE; } }
基於 classloader 機制避免了多線程的同步問題,不過INSTANCE
在類裝載時就實例化,雖然致使類裝載的緣由有不少種,在單例模式中大多數都是調用 getInstance
方法。類在加載時就初始化了,會浪費空間,由於無論你用仍是不用,它都建立出來了,可是由於沒有加鎖,執行效率較高。github
/** * 懶漢式的單例模式-線程不安全的 */ public final class LazyThreadNotSafeSingleton { private static LazyThreadNotSafeSingleton INSTANCE; private LazyThreadNotSafeSingleton() { } public static LazyThreadNotSafeSingleton getInstance() { if (null == INSTANCE) { INSTANCE = new LazyThreadNotSafeSingleton(); } return INSTANCE; } }
/** * 懶漢式的單例模式-線程安全的 */ public final class LazyThreadSafeSingleton { private static LazyThreadSafeSingleton INSTANCE; private LazyThreadSafeSingleton() { } public static synchronized LazyThreadSafeSingleton getInstance() { if (null == INSTANCE) { INSTANCE = new LazyThreadSafeSingleton(); } return INSTANCE; } }
有上述兩種懶漢式單例模式,區別在與靜態工廠方法getInstance
是否加了synchronized
修飾來進行同步,用於支持線程安全。懶漢式,在其加載對象的時候是不會建立對象實例的,只有等它真正使用的時候纔會建立,若是一直沒有使用則一直不會建立,可以避免內存浪費,也就是隻有第一次調用的時候纔會建立。可是加鎖synchronized
就影響了性能和效率,致使getInstance
方法的性能受影響,此種方式也不推薦。尋找一種既能線程安全又能夠延遲加載的方式。設計模式
/** * 雙檢查鎖的單例模式-線程安全 */ public final class DoubleCheckLockingSingleton { private static volatile DoubleCheckLockingSingleton INSTANCE; private DoubleCheckLockingSingleton() { } public static DoubleCheckLockingSingleton getInstance() { // 第一次檢查實例是否存在,若是存在便可返回,不存在則進入同步塊 if (null == INSTANCE) { // 同步塊,線程安全 synchronized (DoubleCheckLockingSingleton.class) { // 第二次檢查實例是否存在,若是還不存在則會真正的建立實例 if (null == INSTANCE) { INSTANCE = new DoubleCheckLockingSingleton(); } } } return INSTANCE; } }
雙檢查加鎖的方式,能實現線程安全,又能減小性能的影響。雙檢查加鎖,旨在每次調用getInstance
方法都須要同步,可是先不會同步,在第一次判斷實例是否存在後,若是不存在才進入同步塊,進入同步塊後,第二次檢查實例是否存在,若是不存在,在同步塊內建立實例。如此只有首次纔會同步,從而減小了屢次在同步狀況下進行判斷所浪費的時間。雙檢查加鎖機制的實現會使用關鍵字volatile,它的意思是:被volatile修飾的變量的值,將不會被本地線程緩存,全部對該變量的讀寫都是直接操做共享內存,從而確保多個線程能正確的處理該變量。可是實現過程稍微複雜點。緩存
/** * 靜態內部類Holder式單例 * * 延遲加載和線程安全 */ public final class LazyInitializationHolderSingleton { private LazyInitializationHolderSingleton() { } public static LazyInitializationHolderSingleton getInstance() { return InstanceHolder.INSTANCE; } /** * 延遲加載 */ private static class InstanceHolder { private static final LazyInitializationHolderSingleton INSTANCE = new LazyInitializationHolderSingleton(); } }
當getInstance
方法第一次被調用的時候,它第一次讀取InstanceHolder.INSTANCE
,致使InstanceHolder
類獲得初始化;而這個類在裝載並被初始化的時候,會初始化它的靜態域,從而建立Singleton的實例,因爲是靜態的域,所以只會在虛擬機裝載類的時候初始化一次,並由虛擬機來保證它的線程安全性。這個模式的優點在於,getInstance
方法並無被同步,而且只是執行一個域的訪問,所以延遲初始化並無增長任何訪問成本。其中使用到類的靜態內部類和多線程缺省同步鎖。安全
靜態內部類微信
靜態內部類指,有static修飾的成員式內部類。若是沒有static修飾的成員式內部類被稱爲對象級內部類。類級內部類至關於其外部類的static成分,它的對象與外部類對象間不存在依賴關係,所以可直接建立。而對象級內部類的實例,是綁定在外部對象實例中的。靜態內部類中,能夠定義靜態的方法。在靜態方法中只可以引用外部類中的靜態成員方法或者成員變量。靜態內部類至關於其外部類的成員,只有在第一次被使用的時候才被會裝載。多線程
多線程缺省同步鎖併發
在多線程開發中,爲解決併發問題,主要是經過使用synchronized來加互斥鎖進行同步控制。可是在某些狀況中,JVM已經隱含地爲您執行了同步,這些狀況下就不用本身再來進行同步控制了。這些狀況包括:
1.由靜態初始化器(在靜態字段上或static{}塊中的初始化器)初始化數據時;
2.訪問final字段時;
3.在建立線程以前建立對象時;
4.線程能夠看見它將要處理的對象時
/** * 採用枚舉類型的單例模式 */ public enum SingletonEnum { INSTANCE; @Override public String toString() { return getDeclaringClass().getCanonicalName() + "@" + hashCode(); } public void something(){ //do something... } }
簡潔,自動支持序列化機制,絕對防止屢次實例化。《高效Java 第二版》中的說法:單元素的枚舉類型已經成爲實現Singleton的最佳方法。用枚舉來實現單例很是簡單,只須要編寫一個包含單個元素的枚舉類型便可。
不建議使用懶漢式,簡單的闊以使用餓漢式。涉及到反序列化建立對象時闊以使用枚舉方式。若是考慮到延遲加載 的話,闊以採用靜態內部類Holder的模式。若是對業務需求有特殊要求的時候闊以採用雙檢查鎖的單例。
歡迎關注瞭解最新動態更新