在Java中,單例有不少種寫法,面試時,手寫代碼環節,除了寫算法題,有時候也會讓手寫單例模式,這裏記錄一下單例的幾種寫法和優缺點。java
public class Singleton { private static Singleton singleton = null; public Singleton() { } /**併發下會產生多個實例*/ public static Singleton getInstance(){ if(singleton == null){ singleton = new Singleton(); } return singleton; } }
上面這種寫法,在併發環境下,會出現多個實例。面試
咱們優化上面的代碼,遇到併發,很容易想到加鎖,把獲取對象的方法加上關鍵字synchronized,這種寫法稱爲懶漢式單例,以下:算法
public class BSingleton { private static BSingleton bSingleton; private BSingleton() { } /** * 整個方法鎖住了,效率較低 * @return */ public synchronized static BSingleton getbSingleton(){ if(bSingleton == null){ bSingleton = new BSingleton(); } return bSingleton; } }
懶漢式的特色是,用到這個實例時纔去調用方法實例化。可是,咱們把整個方法都同步了,效率很低下,咱們能夠繼續優化,只在建立實例的地方加上同步,參考5雙鎖檢驗。安全
餓漢式的特色是:類在加載時就直接初始化了實例。即便沒用到,也會實例化,所以,也是線程安全的單例模式。服務器
public class ESingleton { /**類在加載的時候直接進行初始化*/ private static final ESingleton ESINGLETON = new ESingleton(); private ESingleton() {} /**對外暴露惟一接口 提供單例對象*/ public static ESingleton geteSingleton(){ return ESINGLETON; } }
雙重非空判斷,new對象前加一次鎖。多線程
volatile關鍵字,考慮的是,new關鍵字在虛擬機中執行時其實分爲不少步驟,具體緣由能夠參考深刻理解java虛擬機一書(考慮的是這個new關鍵字字節碼執行時是非原子性的),而volatile關鍵字能夠防止指令重排。併發
public class SynchronizedSingleton { /**volatile防止指令重排*/ private static volatile SynchronizedSingleton singleton; private SynchronizedSingleton() { } /**只是在實例爲空時才進行同步建立 * 爲何作了2次判斷? * A線程和B線程同時進入同步方法0 * 而後都在1位置處判斷了實例爲null * 而後都進入了同步塊2中 * 而後A線程優先進入了同步代碼塊2中(B線程也進入了),而後建立了實例 * 此時,若是沒有3處的判斷,那麼A線程建立實例同時,B線程也會建立一個實例 * 因此,還須要作2次判斷 * */ public static SynchronizedSingleton getInstance(){//0 if(singleton == null){//1 synchronized (SynchronizedSingleton.class){//2 if(singleton == null){//3 singleton = new SynchronizedSingleton();//4 } } } return singleton; } }
public class Singleton { private static class SingletonHolder{ private static final Singleton INSTANCE = new Singleton(); } private Singleton (){} public static Singleton getInstance() { return SingletonHolder.INSTANCE; } }
優勢:因爲靜態內部類跟外部類是平級的,因此外部類加載的時候不會影響內部類,所以實現了lazy loading, 同時也是利用靜態變量的方式,使得INSTANCE只會在SingletonHolder加載的時候初始化一次,從而保證不會有多線程初始化的狀況,所以也是線程安全的。優化
public enum Singleton{ INSTANCE; public void whateverMethod() { } }
這是知名書籍Java Effective推薦的單例實現方式,這種代碼最簡練,而且天生線程安全。阿里雲