單例模式(詳解)

餓漢模式
public
class HungryMode { private static HungryMode hm = new HungryMode(); private HungryMode() { System.out.println("creat hungryMode Instance"); } public static HungryMode getInstance() { return hm; } public static void ceshi() { System.out.println("ceshi"); } public static void main(String[] args) { HungryMode.ceshi(); } }

餓漢式單例,就是一個私有的構造方法加一個私有的靜態當前類實例對象和一個公有的靜態獲取實例方法組成因爲類實例對象爲靜態變量,因此在加載類的時候咱們就會建立類的實例對象,這樣的話比較消耗內存,浪費性能。 html

懶漢模式
public
class LazyMode { private static LazyMode aLazyMode; private LazyMode() { System.out.println("create LazyMode Instance"); } public static LazyMode getInstance() { if(aLazyMode==null) { aLazyMode = new LazyMode(); } return aLazyMode; } public static void main(String[] args) { LazyMode.getInstance(); } }

懶漢式在餓漢式的基礎上作了改進,類實例對象作了懶加載,也就是所謂的延時加載,因此提高了一些性能。編程

多線程的單例
以上的寫法在單線程的編程環境中沒有什麼問題,可是若是是多個線程使用上面的單例模式就會違背單例模式的設計原則,出現多個對象,簡單的作法是在獲取類實例的方法上加同步鎖,而且給類實例對象加上volatile修飾符,volatile能保證對象的可見性,即在工做內存的內容更新能當即在主內存中可見。工做內存是線程獨有的內存,主內存是全部線程共享的內存。還有一個做用是禁止指令重排序優化。你們知道咱們寫的代碼(尤爲是多線程代碼),因爲編譯器優化,在實際執行的時候可能與咱們編寫的順序不一樣。編譯器只保證程序執行結果與源代碼相同,卻不保證明際指令的順序與源代碼相同。這在單線程看起來沒什麼問題,然而一旦引入多線程,這種亂序就可能致使嚴重問題。volatile關鍵字就能夠從語義上解決這個問題。由於同步鎖的機制,多個線程獲取類實例對象會排隊等待獲取鎖,這樣是不必的,由於大多數狀況下類實例對象都已經建立成功了,因此不用進入加鎖的代碼塊,因而就能夠再次改進上面的代碼爲雙重校驗的單例模式,如代碼所示多線程

/**
 * 多線程的單例模式,使用雙重校驗機制
 */
public class DoubleCheckMode {

    private volatile static DoubleCheckMode sDoubleCheckMode ;

    public DoubleCheckMode() {
        System.out.println(" created " + getClass().getSimpleName());
    }

    public static DoubleCheckMode getInstance() {
        if (sDoubleCheckMode == null)
            synchronized (DoubleCheckMode.class) {
                if (sDoubleCheckMode == null) {
                    sDoubleCheckMode = new DoubleCheckMode();
                }
            }
        return sDoubleCheckMode;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    System.out.println("thread" + getId());
                    DoubleCheckMode.getInstance();
                }
            }.start();
        }
    }

}

使用靜態內部類實例單例
這種實現就是利用靜態類只會加載一次的機制,使用靜態內部類持有單例對象,達到單例的效果,直接上代碼吧ide

/**
 * 靜態內部類的方式實現單例,能夠保證多線程的對象惟一性,還有提高性能,不用同步鎖機制
 */
public class InnerStaticMode {

    private static class SingleTonHolder {

        public static InnerStaticMode sInnerStaticMode = new InnerStaticMode();

    }

    public static InnerStaticMode getInstance(){
        return SingleTonHolder.sInnerStaticMode;
    }

}

 

使用枚舉實現單例性能

public enum  EnumMode {

    INSTANCE;

    private int id;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }
}

總結
枚舉實現單例,不推薦在Android平臺使用,由於內存消耗會其餘方式多一些,Android官方也不推薦枚舉,Android平臺推薦雙重校驗或者靜態內部類單例,如今的Android開發環境jdk通常都大於1.5了。因此volatile的問題沒必要擔憂。Java平臺開發的Effective Java一書中推薦使用枚舉實現單例,能夠保證效率,並且還能解決反序列化建立新對象的問題。優化

參考:你真的會寫單例模式嗎——Java實現

相關文章
相關標籤/搜索