Java設計模式--單例模式
直想寫點關於設計模式的東西,卻懶到現在都沒寫過什麽,今天上午看到項目中的代碼,就在這個中午抽出時間寫點東西,拋個項目截圖先:
單例模式:
單例模式,是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱為單例的特殊類。通過單例模式可以保證系統中,應用該模式的類一個類只有一個實例。即一個類只有一個對象實例。
下面介紹Java中常用的單例模式:
一、懶漢模式
這種寫法看上去似乎實現了單例模式,然鵝、實際應用中卻不實用,盡管我所在的項目中有著類似的代碼,我也不得不說確實如此;
優點:實現了按需創建,即懶加載
缺點:線程不安全。在多線程案例中,這種方式並沒有完全實現單例模式。例如當多個線程訪問getSingleton方法時,一起判斷null == singleton,這時就會創建多個Singleton的實例。
實現了線程安全的懶漢模式(兩種方式寫法不同,卻是一致的):
上面兩種寫法雖有差異,但運行效果卻是相同的;
優點:解決了多線程的安全問題
缺點:效率卻低的不要不要的。因為每當有線程訪問時,就會出現線程排隊等候,真正去創建實例的線程只有一個。
現實中也有人通過添加volatile關鍵字對對象實例進行限制,volatile保證其對所有線程的可見性,並且禁止對其進行指令重排序優化。
下面介紹下兼顧線程安全與效率的懶漢模式:
這種方式被稱為雙重檢查鎖/雙重校驗鎖方式,通過兩次判null操作,它解決了線程安全的問題,不會創建多個實例。同時、也提高了線程訪問的效率問題。這種方式可以算作是懶漢模式的最優解,但並非單例模式的最優解。
二、餓漢模式
優點:寫法簡單,一目了然
缺點:無法做到按需創建,即懶加載,增加負載。
註意:這裏的增加負載要看具體情況,就上面的代碼而言,new一個Singleton實例,算不上增加負載,但如果你new的是一個很大的對象,這時你應該考慮這個問題了。
三、靜態內部類
把Singleton實例放到一個靜態內部類中,這樣就避免了靜態實例在Singleton類加載的時候就創建對象,並且由於靜態內部類只會被加載一次,所以這種寫法也是線程安全的。
優點:線程安全,按需創建。
缺點:1、需要額外的工作(Serializable、transient、readResolve())來實現序列化,否則每次反序列化一個序列化的對象實例時都會創建一個新的實例。
2、可能會有人使用反射強行調用我們的私有構造器(如果要避免這種情況,可以修改構造器,讓它在創建第二個實例的時候拋異常)。
四、枚舉
使用枚舉除了線程安全和防止反射強行調用構造器之外,還提供了自動序列化機制,防止反序列化的時候創建新的對象。因此,Effective Java推薦盡可能地使用枚舉來實現單例。
最後,不管采取何種方案,請時刻牢記單例的三大要點:
- 線程安全
- 延遲加載
- 序列化與反序列化安全
Java設計模式--單例模式