單例模式的優缺點緩存
一、時間和空間安全
比較上面兩種寫法:懶漢式是典型的時間換空間,也就是每次獲取實例都會進行判斷,看是否須要建立實例,浪費判斷的時間。固然,若是一直沒有人使用的話,那就不會建立實例,則節約內存空間。併發
餓漢式是典型的空間換時間,當類裝載的時候就會建立類實例,無論你用不用,先建立出來,而後每次調用的時候,就不須要再判斷了,節省了運行時間。性能
二、線程安全spa
(1)從線程安全性上講,不加同步的懶漢式是線程不安全的,好比,有兩個線程,一個是線程A,一個是線程B,它們同時調用getInstance方法,那就可能致使併發問題。以下示例:線程
public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; }
程序繼續運行,兩個線程都向前走了一步,以下:code
public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; }
(2)餓漢式是線程安全的,由於虛擬機保證只會裝載一次,在裝載類的時候是不會發生併發的。xml
(3)如何實現懶漢式的線程安全呢?blog
固然懶漢式也是能夠實現線程安全的,只要加上synchronized便可,以下:內存
可是這樣一來,會下降整個訪問的速度,並且每次都要判斷。那麼有沒有更好的方式來實現呢?
(4)雙重檢查加鎖
可使用"雙重檢查加鎖"的方式來實現,就能夠既實現線程安全,又可以使性能不受到很大的影響。那麼什麼是"雙重檢查加鎖"機制呢?
所謂雙重檢查加鎖機制,指的是:並非每次進入getInstance方法都須要同步,而是先不一樣步,進入方法事後,先檢查實例是否存在,若是不存 在才進入下面的同步塊,這是第一重檢查。進入同步塊事後,再次檢查實例是否存在,若是不存在,就在同步的狀況下建立一個實例,這是第二重檢查。這樣一來, 就只須要同步一次了,從而減小了屢次在同步狀況下進行判斷所浪費的時間。
雙重檢查加鎖機制的實現會使用一個關鍵字volatile,它的意思是:被volatile修飾的變量的值,將不會被本地線程緩存,全部對該變量的讀寫都是直接操做共享內存,從而確保多個線程能正確的處理該變量。
看看代碼可能會更加清楚些。示例代碼以下:
public class Singleton { /** * 對保存實例的變量添加volatile的修飾 */ private volatile static Singleton instance = null; private Singleton(){ } public static Singleton getInstance(){ //先檢查實例是否存在,若是不存在才進入下面的同步塊 if(instance == null){ //同步塊,線程安全地建立實例 synchronized(Singleton.class){ //再次檢查實例是否存在,若是不存在才真正地建立實例 if(instance == null){ instance = new Singleton(); } } } return instance; } }
這種實現方式能夠實現既線程安全地建立實例,而又不會對性能形成太大的影響。它只是在第一次建立實例的時候同步,之後就不須要同步了,從而加快了運行速度。