1. 程式人生 > >驀然回頭-單例模式篇章一

驀然回頭-單例模式篇章一

優缺點 加載 問題 dsi 線程安全 borde important 微軟 16px

單例模式整理

敲了多年代碼後,回頭來看會別有一番滋味在心頭。。

概念

單例模式是為了保證在一個jvm環境下,一個類僅有一個對象。

代碼中常見的懶漢式。餓漢式,這些實現方式可以通過代碼的設計來強制保證的單例,也可以通過文檔,通過編碼約束,可以認為是一個類僅有一個對象。

代碼場景

項目中被封裝到底層的連接功能的類,數據庫連接,socket連接,具有配置功能的類,工具類,輔助系統類,會需要使用單例模式。這些類的特點:是創建和銷毀需要消耗大量的系統資源,或者不需要創建多個對象。

實戰

單例1,餓漢式

/**
 * 最常見的單例,有兩個必不可少的東西
 * 1.私有化類的所有構造函數,以阻止其他代碼在該類的外界去創建對象
 * 2.提供獲取對象的靜態方法,以返回類唯一的對象引用
 *
 * @author
sunyang * @date 2018/11/6 18:44 */
//單例1,通過類的靜態變量來持有一個該類的對象的引用,同時使用final關鍵字來阻止被再次賦值 public class Singleton1 { private final static Singleton1 INSTANCE = new Singleton1(); public Singleton1() { } public static Singleton1 getINSTANCE() { return INSTANCE; } }

單例2,餓漢式

/**
 * 單例2,是使用靜態變量維護該類的引用,但是要將對象創建放在靜態代碼塊中
 *
 * @author
sunyang * @date 2018/11/6 18:49 */
public class Singleton2 { private static final Singleton2 INSTANCE; static { INSTANCE = new Singleton2(); } public Singleton2() { } public static Singleton2 getInstance(){ return INSTANCE; } }

單例3,懶漢式

/**
 * 單例3,使用靜態變量維持類的對象引用,在獲取對象的方法中對象進行判斷和創建
 *
 * @author
sunyang * @date 2018/11/6 18:59 */
public class Singleton3 { public static Singleton3 instance; private Singleton3(){ } private static Singleton3 getInstance(){ if (instance == null){ instance = new Singleton3(); } return instance; } }

分析

單例1和單例2,將對象創建放在類的初始化階段,單例3則是將對象的創建放在類的使用階段。

所以,單例1和單例2稱為餓漢式,單例3稱為懶漢式。優缺點:

餓漢式的優點是簡單易懂,缺點是沒有達到懶加載的效果。如果整個生命周期中自始至終未使用過這個實例,就會比較浪費連接資源和內存。

懶漢式並不復雜,可以起到懶加載的效果。於是,實際開發中更願意使用懶漢式,因為節省內存,懶加載。

聯想

類的生命周期:
1.類的加載:

將類的字節碼文件.class從硬盤載入方法區的過程

2.類的連接:

該過程由三個部分組成:驗證、準備和解析

3.類的初始化:

將靜態變量賦值,執行的順序就是,

父類靜態變量->靜態代碼塊->子類靜態變量->子類靜態代碼塊

餓漢式的對象創建在這個階段

4.類的使用:

類的實例化,懶漢式的對象創建處於這個階段,new關鍵字可以觸發該生命周期

5.類卸載

驗證

/**
 * 如果驗證?
 * 根據類的五個生命周期階段,我們只需要驗證在創建對象之前的
 * 那些操作能夠觸發類的初始化就行。
 *
 * @author sunyang
 * @date 2018/11/8 11:13
 */
public class ValidSingleton1 {
    /**
     * 首先在構造方法裏添加打印語句,打印"init"
     * 再添加靜態方法和一個靜態變量
     *
     */
    private static final ValidSingleton1 INSTANCE = new ValidSingleton1();

    //打印init
    public ValidSingleton1() {
        System.out.println("init");
    }

    public static ValidSingleton1 getINSTANCE() {
        return INSTANCE;
    }
    //靜態方法
    public static final void otherMethod(){

    }
    //靜態變量
    public static final int staticField = 0;
}

開始驗證

/**
 * @author sunyang
 * @date 2018/11/8 11:26
 */
public class ValidSingletonDemo {

    public static void main(String[] args) {
        System.out.println("---------------------start");
        /*1.驗證單例1,只聲明,不會觸發類的初始化階段
        ValidSingleton1 validSingleton1 = null;
        if (null == validSingleton1){
            System.out.println("singleton1 is null");
        }*/
        /*//2.調用類的靜態變量
        System.out.println(ValidSingleton1.staticField);*/

        //3.調用類的靜態方法
//        ValidSingleton1.otherMethod();
        //4.初始化兩次
        ValidSingleton1 validSingleton1 = new ValidSingleton1();
        //5.直接調用
//        ValidSingleton1.getINSTANCE();
        System.out.println("---------------------end");

        /**
         * -------start-------
         * singleton1 is null
         * -------end---------
         *
         * ---------------------start
         *  0
         *   ---------------------end
         *
         *   ---------------------start
         *  init
         *  ---------------------end
         *
         *  ---------------------start
         *  init
         *  init
         *  ---------------------end
         *
         *
         *  ---------------------start
         * init
         * ---------------------end
         */
    }
}

從輸出上看,調用類的靜態方法以下,會觸發類的初始化階段。

發現餓漢式,是不會產生線程安全的問題;

在設計上,懶漢式要優於餓漢式

關聯線程安全

在多線程下,懶漢式會有一定修改。當兩個線程在if(null == instance)語句阻塞的時候,可能由兩個線程進入創建實例,從而返回了兩個對象。對此,我們可以加鎖,保證僅有一個線程處於getInstance()方法中,從而保證了線程一致性。多線程下的單例

/**
 * @author sunyang
 * @date 2018/11/8 12:18
 */
public class Singleton4 {

    private static Singleton4 instance;

    private Singleton4(){

    }
    //需要加上synchronized 同步
    public static synchronized Singleton4 getInstance(){
        if (instance == null){
            instance = new Singleton4();
        }
        return instance;
    }
}

驀然回頭-單例模式篇章一