什麼是單例模式
單例模式,是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例的特殊類。通過單例模式可以保證系統中一個類只有一個實例。即一個類只有一個對象實例
餓漢式——當類被加載的時候便進行初始化單例對象
//直接在加載後初始化
public class SingleDemo{
private static SingleDemo demo = new SingleDemo();
private SingleDemo(){}
public static SingleDemo getDemo(){
return demo;
}
}
//以及通過靜態代碼塊實現
public class SingleDemo{
private static SingleDemo demo = null;
static{
demo = new SingleDemo();
}
private SingleDemo(){}
public static SingleDemo getDemo(){
return demo;
}
}
缺點:
雖然看上去餓漢模式不會有線程安全的問題,但是卻依然有一個隱含的漏洞,那就是當有多個類加載器對該對象進行裝載的時候,便會初始化多個demo對象。例如一些servlet容器對每個servlet使用完全不同的類裝載器,這樣的話如果有兩個servlet訪問一個單例類,它們就都會有各自的實例。
懶漢式——當需要使用單例對象時進行初始化操作(多版本)
版本1:
public class SingleDemo{
private static SingleDemo demo = null;
private SingleDemo(){}
public static SingleDemo getDemo(){
if(demo == null){
demo = new SingleDemo();
}
return demo;
}
}
//這是最簡單的版本,但是有個致命的問題就是,它不是一個線程安全的例子,也就是說在多線程環境下,並不總能保證單例。
版本2:
public class SingleDemo{
private static SingleDemo demo = null;
private SingleDemo(){}
public synchronized static SingleDemo getDemo(){
if(demo == null){
demo = new SingleDemo();
}
return demo;
}
}
//通過對方法上鎖,實現的線程安全的獲取單例的操作,但是很遺憾,因爲這個方法在絕大多數情況下是不需要同步的,加鎖嚴重降低了程序執行的效率,所以不提倡使用。
版本3:
public class SingleDemo{
private static SingleDemo demo = null;
private SingleDemo(){}
public static SingleDemo getDemo(){
if(demo == null){
synchronized(SingleDemo.class) {
demo = new SingleDemo();
}
}
return demo;
}
}
//對版本2進行優化後,可以明顯提高程序的效率,但是很遺憾,這段代碼並不能保證線程安全。
版本4:
public class SingleDemo{
private static SingleDemo demo = null;
private SingleDemo(){}
public static SingleDemo getDemo(){
if(demo == null){
synchronized(SingleDemo.class) {
if(demo == null) {
demo = new SingleDemo();
}
}
}
return demo;
}
}
//這是上述版本的最終形態,俗稱雙檢鎖模式,通過雙重校驗,達到了線程安全的目的,同時避免了代碼頻繁上鎖解鎖的過程
版本5:
public class SingleDemo{
private SingleDemo(){}
private static class Singleton{
private static final SingleDemo DEMO = new SingleDemo();
}
public static SingleDemo getDemo(){
return Singleton.DEMO;
}
}
//這樣至少看起來代碼簡潔不少,不僅達到了懶加載的效果,還實現了線程安全。
版本6:
public enum SingleDemo{
DEMO;
public static SingleDemo getDemo(){
return DEMO;
}
}
//最後這種是通過枚舉實現的單例,但是現實用到的不多,該模式爲Effective Java作者Josh Bloch 所推崇,它不僅能避免多線程同步問題,而且還能防止反序列化重新創建新的對象(如果Singleton實現了java.io.Serializable接口,那麼這個類的實例就可能被序列化和復原,如果你序列化一個單例類的對象,接下來複原多個那個對象,那你就會有多個單例類的實例)。