設計模式系列-單例模式

1 單例模式

單例模式:確保某一個類只有一個實例,自行實例化并向整個系統提供這個實例。

使用場景

確保某個類只有一個對象的場景,避免產生多個對象消耗資源,或者某個對象應該有且只有一個的場景。

特點

1.構造函數不對外開放,一般為private

2.通過靜態方法返回單例類的對象

3.確保對象只有一個,尤其是在多線程的情況下

4.確保單例類對象在反序列化時不會重新創建對象

寫單例

一:餓漢模式

    public class Singleton {
        private static final Singleton singleton = new Singleton();
        private Singleton(){
        }
        public static Singleton getInstance(){
            return singleton;
        }
        //  do.....
    }

二:懶漢模式

    public class SingletonV2 {
        private static SingletonV2 singletonV2 = null;
        private SingletonV2(){
        }
        public static synchronized SingletonV2 getInstance(){
            if(singletonV2 != null){
                singletonV2 = new SingletonV2();
            }
            return singletonV2;
        }
        // do....
    }

優點:只有在使用時才會被實例化
缺點:每次使用都要進行同步,即使單例已經存在,造成不必要的同步開銷

三:DCL實現單例(雙鎖)

    public class SingletonV3 {
        private static SingletonV3 singletonV3 = null;
        private SingletonV3(){
        }
        public static SingletonV3 getInstance(){
            if(singletonV3 == null){
                synchronized (SingletonV3.class){
                    if(singletonV3 == null){
                        singletonV3 = new SingletonV3();
                    }
                }
            }
            return singletonV3;
        }
        // do ....
    }

DCL失效問題:

singletonV3 = new SingletonV3(); 一句代碼,卻不是原子性操作,會被編譯成多條匯編指令,但大致作了三件事:

1.給SingletonV3的實例分配內存;

2.調用SingletonV3的構造函數,初始化字段;

3.將singletonV3指向分配的內存空間(這時singletonV3 不為 null)

JDK1.5之前java編譯器允許處理器亂序執行,上面2,3的執行順序是無法保證的,所以執行順序可能是1-2-3,也可能是1-3-2,如果是后者,則有可能出現3執行完畢,2還未執行,此時singletonV3已經不為null,但另一個線程取走singletonV3,使用就會出錯。

jdk1.5之后,使用volatile,修改為private volatile static SingletonV3 singletonV3 = null;就可以保證singletonV3保證每次都是從主內存中讀取。

能夠在絕大多數情況保證單例對象的唯一性,除非在并發場景比較復雜或JDK低于6的版本下使用。

四:靜態內部類實現

    public class SingletonV4 {
        private SingletonV4(){
        }
        public static SingletonV4 getInstance(){
            return SingletonHolder.singletonV4;
        }
        private static class SingletonHolder{
            private static final SingletonV4 singletonV4 = new SingletonV4();
        }
    }

第一次加載SingletonV4時并不會初始化singletonV4,只有第一次調用getInstance()才會導致加載SingletonHolder類,線程安全,而且保證對象的唯一性,切延遲實例化,推薦的單例實現方式。

五:枚舉實現單例

public enum SIngletonEnum {
    Instance;
    // do something
    private int num = 10;
    public int doSomething(){
        return num;
    }
    //
}

枚舉單例的最大特點就是簡單,枚舉不僅能夠有字段,還能夠有方法,枚舉單例是線程安全的。

反序列化

序列化操作提供了一個很特別的鉤子(hook)-類中具有一個私有的被實例化的方法readresolve(),這個方法可以確保類的開發人員在序列化將會返回怎樣的object上具有發言權。

     private Object readResolve() throws ObjectStreamException {
         return getInstance();
     }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容