單例模式優化

一、前言:

  1. 單例模式,是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例的特殊類。通過單例模式可以保證併發環境下中,應用該模式的一個類只有一個實例。即一個類只有一個對象實例。
  2. 常見的單例模式:
    餓漢模式: 在程序啓動時即創建對象實例。
    懶漢模式:僅當程序中使用到改對象時,纔回去創建對象。

二、單例模式實例:

1. 餓漢模式,程序啓動,對象實例被創建 【不推薦】:

/**
 * @Des: 餓漢模式
 */
public class SingleTest01 {

    // 靜態變量系統啓動就會被加載
    private static SingleTest01 singleTest = new SingleTest01();

    // 私有化構造方方
    private SingleTest01(){
        System.out.println(" init ");
    }

    // 返回對象
    public static SingleTest01 newSingleTest(){
        return singleTest;
    }

}
優點:
  1. 代碼實現簡單,利用類加載機制保證線程安全。
缺點:
  1. 在程序啓動時,就已經完成實例化,如果對象沒有使用,會造成內存浪費。
  2. 如果對象在啓動時存在一些耗時操作,會影響到我們程序啓動時間
2、 懶漢模式, 當程序用到該實例時去創建對象,使用 synchronized 對獲取方法進行加鎖,實現併發安全【不推薦】:
/**
 * @Des: 懶漢模式
 */
public class SingleTest02 {

    // 靜態變量保存對象
    private static SingleTest02 singleTest = null;

    // 私有化構造方法
    private SingleTest02(){
        System.out.println(" init ");
    }

    // synchronized 修飾方法保證併發安全
    public static synchronized SingleTest02 newSingleTest() {
        if (singleTest == null){
           singleTest = new SingleTest02();
        }
        return singleTest;
    }
}

優點:
  1. 就是餓漢模式的缺點
缺點:
  1. 效率太低,加鎖的細粒度太大,其實僅僅在第一次創建對象時需要加鎖,實例化完成之後,獲取的時候完全沒有必要加鎖。

三、單例模式 – 懶漢模式優化:

第一種優化方案,縮小鎖粒度:
  1. 使用 volatile 保證線程可見性
  2. 對象爲被實例化時,通過代碼快進行加鎖,雙重檢驗保證最終結果單例。

/**
 * @Des: 懶漢模式
 */
public class SingleTest03 {

    // 靜態變量保存對象, volatile 保證每個線程讀取最新的數據
    private static volatile SingleTest03 singleTest = null;

    // 私有化構造方法
    private SingleTest03(){
        System.out.println(" init ");
    }

    // 獲取實例對象
    public static  SingleTest03 newSingleTest() {
        if (singleTest == null){
            // singleTest 爲空,表示沒有初始化,將當前類加鎖。
            synchronized(SingleTest03.class){
                // 雙重校驗,避免第一個if之後有多個線程在等待。
                if (singleTest == null){
                    singleTest = new SingleTest03();
                }
            }
        }
        return singleTest;
    }

}

第二種優化方案,使用靜態內部類:
  1. 利用類加載機制,保證實例化對象時僅有一個線程,內部類僅在使用時會初始化靜態屬性,實現了懶加載,效率高, 和第一中優化方案差不多,代碼如下:

/**
 * @Des: 懶漢模式
 */
public class SingleTest04 {

    private static class SingletonInstance{
        private final static SingleTest04 SINGLETON = new SingleTest04();
    }

    private SingleTest04(){
        System.out.println(" init ");
    }

    // 獲取實例對象
    public static SingleTest04 newSingleTest() {
        return SingletonInstance.SINGLETON;
    }
}


四、目前推薦的單例的創建方式:

  1. 上面的方式雖然都實現了單例模式,各有各自的優缺點。但是他們都有一個公共的 缺點,無法防止暴力創建對象,例如: 反射、序列化、克隆。
  2. 在 《Effective Java》作者的Josh Bloch提倡我們使用枚舉的方式來創建單例對象,使用非常簡單。
    關於枚舉的博文: https://blog.csdn.net/zhangyong01245/article/details/103322007
  3. 代碼示例:

/**
 * @Des: 枚舉實現單例
 */
public enum Singleton {

    // Singleton的單例對象,枚舉被加載時,由JVM創建,線程安全
    INSTANCE;

    // 單例對象中的方法
    public void print(){
        System.out.println(this.hashCode());
    }
}

測試類:


public class Test {
    public static void main(String[] args) {
        for (int i =0; i<10;i++){
            new Thread(new Runnable() {
                public void run() {
                    Singleton singleton = Singleton.INSTANCE;
                    singleton.print();
                }
            }).start();
        }

    }
}

打印結果:
在這裏插入圖片描述

End。

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