java設計模式-單例模式

對一些重要資源的訪問, 有時需要實例創建只有一份, 那麼實例創建就需要用到單例模式 .


惡漢式:

在類初始化時候就加載了對象, 可以不用考慮多線程問題 .
缺點 : 如果對象創建比較耗資源, 提前加載會耗費性能 .

/**
 * 惡漢式
 * @author 張延
 *  */
public class SingleInstance {
    // 類初始化就加載這個對象, JVM初始化是線程安全的
    private static SingleInstance singleInstance = new SingleInstance();
    // 私有化構造器
    private SingleInstance(){};
    // 提供一個公共的獲取對象方法
    public static SingleInstance getInstance() {
        return singleInstance;
    }
}

懶漢式:

  • 不加鎖
    多線程情況會出現問題, 可能會導致兩份實例創建
/**
 * 懶漢式
 * @author 張延
 *  */
public class SingleInstance {

    private static SingleInstance singleInstance = null;
    private SingleInstance(){};
    public static SingleInstance getInstance() {
        if(singleInstance == null) {
            singleInstance = new SingleInstance();
        }
        return singleInstance;
    }
}
  • 加鎖方式
    針對上面不安全方式改進, 但是這種synchronized修飾方法會導致性能降低, 因爲getInstance是一個頻繁被使用方法,
/**
 * 加鎖的懶漢式
 * @author 張延
 *  */
public class SingleInstance1 {

    private static SingleInstance1 singleInstance = null;
    private SingleInstance1() {};
    // 在方法上加鎖,限制多個線程同時進入,導致實例化不是一份
    public synchronized static SingleInstance1 getInstance() {
         if(singleInstance == null) {
             singleInstance = new SingleInstance1();
         }
         return singleInstance;
    }

}
  • 雙重鎖檢查
    將鎖的粒度降低, 多線程都可以進去判斷”null == singleInstance” , 只有當沒有實例創建纔會進入同步快, 再次檢查是否創建, 如果沒有創建,纔會創建這個實例 .
/**
 * 雙重鎖檢查的懶漢式
 * @author 張延
 *
 */
public class SingleInstance2 {

    private volatile static SingleInstance2 singleInstance = null;
    private SingleInstance2() {};
    public static SingleInstance2 getInstance() {
        // 多線程可以進來判斷是否爲null,只有當沒有創建實例,纔會進入同步代碼快
        if(singleInstance == null) {
            // 將鎖的粒度降低,
            synchronized (SingleInstance2.class) {
                if(singleInstance == null) {
                    singleInstance = new SingleInstance2();
                }
            }
        }
        return singleInstance;
    }
}

靜態內部類方式:

基本具備很多優點 , 高校調用 , 延時加載 , 線程安全
SingleInstance類被加載, 但是instance不會被初始化, 可以做到延時加載, 只有當主動獲取實例變量, 纔會顯示加載類, 因爲JVM是天然線程安全的, 不會加載兩份, 所以實例化instance很耗資源, 應該讓他延時加載 .

public class SingleInstance {

    // 初始化SingleInstance的時候,內部類InnerClass不會被加載,只有在被主動使用纔會加載
    private static class InnerClass {
        private static SingleInstance singleInstance = new SingleInstance();
    }
    private SingleInstance() {};

    public static SingleInstance getInstance() {
        // 當這裏主動使用, 纔會加載InnerClass,纔會創建SingleInstance實例
        return InnerClass.singleInstance;
    }
}

總結:

餓漢式 : 線程安全 , 調用效率高 , 但是不能延時加載
懶漢式(加鎖) : 線程安全 , 調用效率不高 , 但是可以延時加載
靜態內部類 : 線程安全 , 調用效率高 , 並且還可以延時加載

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