1. 程式人生 > 實用技巧 >04-單例模式的幾種寫法

04-單例模式的幾種寫法

示例中的SingleUtil類是個空類, 只是為了模擬建立單例的存在而已....

懶漢式

package demo.java.jiangkd.singleton.example;

/**
 * 懶漢式<br>
 * 懶漢模式中單例是在需要的時候才去建立的,如果單例已經建立,再次呼叫獲取介面將不會重新建立新的物件,而是直接返回之前建立的物件<br>
 * 好處是更啟動速度快、節省資源,一直到例項被第一次訪問,才需要初始化單例<br>
 * 但是這裡的懶漢模式並沒有考慮執行緒安全問題,在多個執行緒可能會併發呼叫它的getInstance()方法,導致建立多個例項
 * 
 * 
@author jiangkd * @date 2020/09/10 */ public class SingleDemo1 { private SingleDemo1() {} private static SingleUtil singleUtil = null; public static SingleUtil getInstance() { // if (null == singleUtil) { singleUtil = new SingleUtil(); } return singleUtil; } }

餓漢式

package demo.java.jiangkd.singleton.example;

/**
 * 餓漢式<br>
 * 餓漢模式是最簡單的一種實現方式,餓漢模式在類載入的時候就對例項進行建立,例項在整個程式週期都存在。<br>
 * 好處是隻在類載入的時候建立一次例項,不會存在多個執行緒建立多個例項的情況,避免了多執行緒同步的問題<br>
 * 缺點也很明顯,即使這個單例沒有用到也會被建立,而且在類載入之後就被建立,記憶體就被浪費了。<br>
 * 值得注意的時,單執行緒環境下,餓漢與懶漢在效能上沒什麼差別;但多執行緒環境下,由於懶漢需要加鎖,餓漢的效能反而更優。
 * 
 * 
@author jiangkd * @date 2020/09/10 */ public class SingleDemo2 { private SingleDemo2() {} private static SingleUtil singleUtil = new SingleUtil(); public static SingleUtil getInstance() { // return singleUtil; } }

懶漢加鎖式

package demo.java.jiangkd.singleton.example;

/**
 * 懶漢加鎖式<br>
 * 加鎖的懶漢模式看起來即解決了執行緒併發問題,又實現了延遲載入,然而它存在著效能問題,依然不夠完美<br>
 * synchronized修飾的同步方法比一般方法要慢很多,如果多次呼叫getInstance(),累積的效能損耗就比較大了
 * 
 * @author jiangkd
 * @date 2020/09/10
 */
public class SingleDemo3 {

    private SingleDemo3() {}

    private static SingleUtil singleUtil = null;

    /**
     * 方法進行加鎖
     * 
     * @return
     */
    public static synchronized SingleUtil getInstance() {
        //
        if (null == singleUtil) {
            singleUtil = new SingleUtil();
        }
        return singleUtil;
    }
}

懶漢雙重加鎖式

package demo.java.jiangkd.singleton.example;

/**
 * 懶漢雙重加鎖式<br>
 * 為了防止new SingleUtil被執行多次,因此在new操作之前加上Synchronized 同步鎖,鎖住整個類(注意,這裡不能使用物件鎖)<br>
 * 進入Synchronized 臨界區以後,還要再做一次判空。因為當兩個執行緒同時訪問的時候,執行緒A構建完物件,執行緒B也已經通過了最初的判空驗證,不做第二次判空的話,執行緒B還是會再次構建instance物件<br>
 * JVM編譯器的指令重排會導致併發問題,所以加入volatile關鍵字防止指令重排
 * 
 * @author jiangkd
 * @date 2020/09/10
 */
public class SingleDemo4 {

    private SingleDemo4() {}

    /**
     * 使用volatile修飾
     */
    private static volatile SingleUtil singleUtil = null;

    /**
     * 程式碼塊進行加鎖
     * 
     * @return
     */
    public static SingleUtil getInstance() {
        //
        if (null == singleUtil) {
            synchronized (SingleDemo4.class) {
                if (null == singleUtil) {
                    singleUtil = new SingleUtil();
                }
            }
        }
        return singleUtil;
    }
}

靜態內部類

package demo.java.jiangkd.singleton.example;

/**
 * 靜態內部類<br>
 * 利用了類載入機制來保證只建立一個instance例項<br>
 * 與餓漢模式一樣,也是利用了類載入機制,因此不存在多執行緒併發的問題<br>
 * 不一樣的是,它是在內部類裡面去建立物件例項<br>
 * 這樣的話,只要應用中不使用內部類,JVM就不會去載入這個單例類,也就不會建立單例物件,從而實現懶漢式的延遲載入<br>
 * 也就是說這種方式可以同時保證延遲載入和執行緒安全
 * 
 * @author jiangkd
 * @date 2020/09/10
 */
public class SingleDemo5 {

    private SingleDemo5() {}

    private static class SingleUtilHolder {
        public static SingleUtil instance = new SingleUtil();
    }

    public static SingleUtil getInstance() {
        //
        return SingleUtilHolder.instance;
    }
}

列舉

package demo.java.jiangkd.singleton.example;

/**
 * 列舉<br>
 * 用列舉實現單例模式,相當好用,但可讀性是不存在的<br>
 * 醜陋但好用的語法糖
 * 
 * @author jiangkd
 * @date 2020/09/10
 */
public enum SingleDemo6 {

    INSTANCE;

    private SingleUtil singleUtil;

    private SingleDemo6() {
        singleUtil = new SingleUtil();
    }

    public SingleUtil getInstance() {
        return singleUtil;
    }

    public static void main(String[] args) {
        //
        SingleUtil singleUtil = SingleDemo6.INSTANCE.getInstance();
    }
}