標籤:工作管理員 log java 集中管理 基礎 實現 介面 single 列印
一、概念
java中單例模式是一種常見的設計模式,單例模式分五種:懶漢式單例、餓漢式單例、靜態內部類單例、枚舉單例和雙重校正鎖單例。
單例模式有以下特點:
1、單例類只能有一個執行個體。
2、單例類必須自己建立自己的唯一執行個體。
3、單例類必須給所有其他對象提供這一執行個體。
單例模式確保某個類只有一個執行個體,而且自行執行個體化並向整個系統提供這個執行個體。在電腦系統中,資源回收筒、工作管理員、線程池、緩衝、日誌對象、對話方塊、印表機、顯卡的驅動程式對象常被設計成單例。這些應用都或多或少具有資源管理員的功能。每台電腦可以有若干個印表機,但只能有一個Printer Spooler,以避免兩個列印工作同時輸出到印表機中。每台電腦可以有若干通訊連接埠,系統應當集中管理這些通訊連接埠,以避免一個通訊連接埠同時被兩個請求同時調用。總之,選擇單例模式就是為了避免不一致狀態,避免爭出多頭。
二、五種單例模式的詳解
1、懶漢式單例
class LazySingleton{ private static LazySingleton singleton; private LazySingleton(){
} public static LazySingleton getInstance(){ if(singleton==null){ singleton=new LazySingleton(); } return singleton; } }
Singleton通過將構造方法限定為private避免了類在外部被執行個體化,在同一個虛擬機器範圍內,Singleton的唯一執行個體只能通過getInstance()方法訪問。(事實上,通過Java反射機制是能夠執行個體化構造方法為private的類的,那基本上會使所有的Java單例實現失效。此問題在此處不做討論,姑且掩耳盜鈴地認為反射機制不存在。)
但是以上實現沒有考慮安全執行緒問題。所謂安全執行緒是指:如果你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。如果每次運行結果和單線程啟動並執行結果是一樣的,而且其他的變數的值也和預期的是一樣的,就是安全執行緒的。或者說:一個類或者程式所提供的介面對於線程來說是原子操作或者多個線程之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。顯然以上實現並不滿足安全執行緒的要求,在並發環境下很可能出現多個Singleton執行個體。
我們可以對懶漢式進行改進,如下所示:
public class Singleton { private static Singleton instance=null; private Singleton(){ } public static synchronized Singleton getInstance(){ if(instance==null){ instance=new Singleton(); } return instance; }}
在懶漢式的基礎上加上了同步鎖,使得在多線程的情況下可以用。例如:當兩個線程同時想建立執行個體,由於在一個時刻只有一個線程能得到同步鎖,當第一個線程加上鎖以後,第二個線程只能等待。第一個線程發現執行個體沒有建立,建立之。第一個線程釋放同步鎖,第二個線程才可以加上同步鎖,執行下面的代碼。由於第一個線程已經建立了執行個體,所以第二個線程不需要建立執行個體。保證在多線程的環境下也只有一個執行個體。
缺點:每次通過getInstance方法得到singleton執行個體的時候都有一個試圖去擷取同步鎖的過程。而眾所周知,加鎖是很耗時的。能避免則避免。
2、餓漢式單例(建議使用)
class HungrySingleton{ private static HungrySingleton singleton=new HungrySingleton(); private HungrySingleton(){} public static HungrySingleton getInstance(){ return singleton; }}
初試化靜態singleton建立一次。如果我們在Singleton類裡面寫一個靜態方法不需要建立執行個體,它仍然會早早的建立一次執行個體。而降低記憶體的使用率。
缺點:沒有lazy loading的效果,從而降低記憶體的使用率。
3、靜態內部類單例
class InternalSingleton{ private static class SingletonHolder{ private final static InternalSingleton INSTANCE=new InternalSingleton(); } private InternalSingleton(){} public static InternalSingleton getInstance(){ return SingletonHolder.INSTANCE; }}
定義一個私人的內部類,在第一次用這個嵌套類時,會建立一個執行個體。而類型為SingletonHolder的類,只有在Singleton.getInstance()中調用,由於私人的屬性,他人無法使用SingleHolder,不調用Singleton.getInstance()就不會建立執行個體。
優點:載入時不會初始化靜態變數INSTANCE,因為沒有主動使用,達到Lazy loading
4.枚舉單例
enum EnumSingleton{ INSTANCE; public void doSomeThing(){ }}
《Effective Java》作者推薦使用的方法,優點:不僅能避免多線程同步問題,而且還能防止還原序列化重新建立新的對象
五、 雙重校正鎖單例 ,前後兩次判斷執行個體是否存在
class LockSingleton{ private volatile static LockSingleton singleton; private LockSingleton(){} //詳見:http://www.ibm.com/developerworks/cn/java/j-dcl.html public static LockSingleton getInstance(){ if(singleton==null){ synchronized(LockSingleton.class){ if(singleton==null){ singleton=new LockSingleton(); } } } return singleton; } }
只有當singleton為null時,需要擷取同步鎖,建立一次執行個體。當執行個體被建立,則無需試圖加鎖。
缺點:用雙重if判斷,複雜,容易出錯。
Java單例模式詳解