java單例模式的5種實現方式

簡介
單例模式是指在程序中只有一個實例存在。並且在程序運行中的任何時候都可以獲取該實例對象。

場景
1.Android中數據庫多線程讀寫時,保證每個線程使用同一個SQLiteDatabase對象,否則會報錯“database is locked”;
2.Android常用框架EventBus中使用了單例模式。例如,當我們獲取EventBus對象時,會使用的EventBus.getDefault(),當我們查看它的源碼的時候,我們發現

    static volatile EventBus defaultInstance;
    ...//此處省略
    ...
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            Class var0 = EventBus.class;
            synchronized(EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }

        return defaultInstance;
    }

也就是EventBus使用了雙重校驗鎖的單例模式。此模式稍候介紹。

單例模式的實現方式

1.懶漢模式
特點:懶加載,不使用不創建,線程不安全

public class Singleton {
    private static Singleton mSingleton = null;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (mSingleton == null)
            mSingleton = new Singleton();
        return mSingleton;
    }

}

2.餓漢模式
特點:類加載的時候就創建對象,線程安全,沒有懶加載的靈活性,可能造成內存浪費

public class Singleton {
    private static Singleton mSingleton = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {
        return mSingleton;
    }

}

3.雙重校驗鎖
特點:可以保證線程安全,但是需要使用volatile(JDK1.5引入)修飾對象的引用,因爲只是使用雙重校驗鎖時,也不能保證它是一個線程安全的單例模式。因爲JMM會有指令重排序機制。當我們創建一個對象的時候,JVM內部會將new Object()這條語句轉化爲若干指令,舉例:
指令1:先爲對象分配內存空間
指令2:初始化對象
指令3:設置對象所在的內存地址
以上是正常情況,如果發生了指令重排,可能指令3就會排在指令2的前面。
也就是先分配了內存地址,後初始化的對象。如果這時線程A指令執行的順序是1-3-2,那麼,當線程A執行到指令3的時候,mSingleton不爲null了。如果線程B執行到第一個if判斷的時候,發現mSingleton不爲空,這時就會出現問題。(見下面代碼)

public class Singleton {
    private static Singleton mSingleton = null;

    private Singleton() {

    }

    public Singleton getInstance() {
        if (mSingleton == null) {
            synchronized (Singleton.this) {
                if (mSingleton == null)
                    mSingleton = new Singleton();
            }
        }
        return mSingleton;
    }
}

使用volatile關鍵字後,就可以禁止指令重排序(增加了內存屏障)
正確代碼:

public class Singleton {
    private volatile static Singleton mSingleton = null;

    private Singleton() {

    }

    public Singleton getInstance() {
        if (mSingleton == null) {
            synchronized (Singleton.this) {
                if (mSingleton == null)
                    mSingleton = new Singleton();
            }
        }
        return mSingleton;
    }
}

4.靜態內部類
特點:類加載的時候就創建對象(此特點跟餓漢模式一樣),線程安全;懶加載,不使用不創建,減少內存開銷。

public class Singleton {

    private Singleton() {
    }

    private static class SingleInstance {
        private static final Singleton singleton = new Singleton();
    }

    public static final Singleton getInstance() {
        return SingleInstance.singleton;
    }
}

5.枚舉單例模式
特點:線程安全,防止了反序列化重新創建的新對象

public enum  EmumSingleton {
    SINGLETON;

    EmumSingleton() {
    }

    public void doSomething(){
        System.out.println("單例模式");
    }
}

Over…

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章