java基礎_設計模式_單例模式二(詳解6種寫法)

大家好,我是野豬。

題目:設計一個類,我們只能生成該類的一個實例。

第一種:只適用於單線程環境

單利模式最簡單的也是不會用的寫法

特徵:只適用於單線程環境

問題:當2個線程同時運行到instance·是否爲null的if語句,並且instance的確沒有創建的時候 那麼2個線程都會創建一個實例。

java代碼:

public class Singleton1 {

    private Singleton1() {
    }

    private static Singleton1 singleton1 = null;

    public static Singleton1 getInstance() {
        if (singleton1 == null) {
            singleton1 = new Singleton1();
        }
        return singleton1;
    }
}

第二種:多線程環境中能工作但是效率不高

雙重加鎖 適合多線程但是因爲加鎖耗時導致效率很低
特徵:加上了同步鎖
問題:由於在一個時刻只有一個線程能拿到同步鎖,當A線程拿到同步鎖的時候,B線程只能等待
當A線程發現沒有創建實例就會去創建實例,代碼走完,然後A線程釋放同步鎖;然後B線程家加上同步鎖,並運行接下來的代碼;
現在存在一個問題就是A線程加上同步鎖並創建完實例後B線程完全沒有必要再加鎖走判斷有沒有實例了。 ps:加鎖是一個非常耗時的操作,在沒有必要的時候應儘量避免。

java代碼:

public class Singleton2 {

    private Singleton2() {
    }

    private static Singleton2 singleton2 = null;

    public static Singleton2 getInstance() {
        //加上同步鎖
        synchronized (Singleton2.class) {
            if (singleton2 == null) {
                singleton2 = new Singleton2();
            }
        }

        return singleton2;
    }
}


第三種:加同步鎖前後兩次判斷實例是否已經存在

特徵:爲了避免A線程獲取到同步鎖後創建了實例,然後釋放鎖;B線程獲取到同步鎖,接着走判斷的是否創建實例的情況;因爲加鎖耗時。
加鎖之前判斷一次有沒有實例;
在首次沒有創建實例的情況下,現在A線程獲取同步鎖創建實例,釋放鎖;B線程線先判斷有無實例,有則不用運行接下來的代碼了。

Java代碼:

public class Singleton3 {

    private Singleton3() {
    }

    private static Singleton3 singleton3 = null;

    public static Singleton3 getInstance() {
        if (singleton3 == null) {
            //加上同步鎖
            synchronized (Singleton3.class) {
                if (singleton3 == null) {
                    singleton3 = new Singleton3();
                }
            }
        }

        return singleton3;
    }
}
雙重加鎖是比較常用的一種寫法,確保了多線程環境下只創建一個實例,並且用兩個if判斷提高效率。

第四種:餓漢式單列

特徵:在類初始化的時候 已經自行實例化; 
問題:並不是當你需要才實例化 降低內存的使用率
ps:Java中的靜態代碼塊是在虛擬機加載類的時候,就執行的,而且只執行一次。
     非靜態代碼塊是在類new一個實例的時候執行,而且是每次new對象實例都會執行。

java代碼:

public class Singleton4 {

    private Singleton4() {
    }

    private static Singleton4 singleton4 = new Singleton4();

    public static Singleton4 getInstance() {

        return singleton4;
    }
}

第五種:餓漢式單列的變種 靜態代碼塊

特徵:跟第四種是一樣的 都是在類初始化時即實例化instance

java代碼:

public class Singleton5 {

    private Singleton5() {
    }

    private static Singleton5 singleton5 = null;

    static {
        singleton5 = new Singleton5();
    }

    public static Singleton5 getInstance() {

        return singleton5;
    }
}

第六種:靜態內部類

特徵:靜態內部類 改善了3和4的餓漢式 因爲3、4 都是類加載的時候就實例化instance 降低了內存使用效率。
只有在真正需要的時候纔會創建實例,提高空間使用效率
因爲內部類Nested{}沒有被主動使用,只有調用getInstance()的時候纔會去實例化instance。
這就避免了類加載的時候就實例化instance
舉例:假設實例化instance很消耗資源,而類中有其他的方法我需要調用,如果類一加載就實例化的話,那太消耗資源了。所以做到真正需要的時候纔去創建實例最好。

java代碼:

public class Singleton6 {

    private Singleton6() {
    }

    public static Singleton6 getInstance() {

        return Nested.instance;
    }

    private static class Nested {

        public static Singleton6 instance = new Singleton6();

    }
}

以上是自己簡單總結的常見的單例模式的寫法,希望對大家有所幫助。在上一篇中簡單的說了一下單例模式存在的因素和使用的地方,鏈接如下:

點擊打開鏈接



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