Java單例模式的6種寫法


在Java中,單例有很多種寫法,面試時,手寫代碼環節,除了寫算法題,有時候也會讓手寫單例模式,這里記錄一下單例的幾種寫法和優缺點。

  • 初級寫法
  • 懶漢式
  • 餓漢式
  • 雙鎖檢驗
  • 內部類
  • 枚舉式

1.初級寫法

public class Singleton {

    private static Singleton singleton = null;

    public Singleton() {
    }

    /**並發下會產生多個實例*/
    public static Singleton getInstance(){
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

 上面這種寫法,在並發環境下,會出現多個實例。

2.懶漢式

我們優化上面的代碼,遇到並發,很容易想到加鎖,把獲取對象的方法加上關鍵字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雙鎖檢驗。

3.餓漢式

餓漢式的特點是:類在加載時就直接初始化了實例。即使沒用到,也會實例化,因此,也是線程安全的單例模式。

public class ESingleton {

    /**類在加載的時候直接進行初始化*/
    private static final ESingleton ESINGLETON = new ESingleton();

    private ESingleton() {}

    /**對外暴露唯一接口  提供單例對象*/
    public static ESingleton geteSingleton(){
        return ESINGLETON;
    }
}

4.雙鎖檢驗

雙重非空判斷,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;
    }
}

5.內部類

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加載的時候初始化一次,從而保證不會有多線程初始化的情況,因此也是線程安全的。

6.枚舉式

public enum Singleton{
    INSTANCE;
    public void whateverMethod() {
        
    }
}

 這是知名書籍Java Effective推薦的單例實現方式,這種代碼最簡練,並且天生線程安全。

推廣:一個好消息

同學,你造嗎?阿里雲和騰訊雲已白菜價,雲服務器低至不到300元/年。這里有一份雲計算優惠活動列表,來不及解釋了,趕緊上車!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM