java設計模式——單例設計模式八種方式

緒論

設計模式分爲三種類型,共 23 種

  1. 創建型模式:單例模式、抽象工廠模式、原型模式、建造者模式、工廠模式。
  2. 結構型模式:適配器模式、橋接模式、裝飾模式、組合模式、外觀模式、享元模式、代理模式。
  3. 行爲型模式:模版方法模式、命令模式、訪問者模式、迭代器模式、觀察者模式、中介者模式、備忘錄模式、 解釋器模式(Interpreter 模式)、狀態模式、策略模式、職責鏈模式(責任鏈模式)。

單例模式

1、介紹

所謂類的單例設計模式,就是採取一定的方法保證在整個的軟件系統中,對某個類只能存在一個對象實例,並且該類只提供一個取得其對象實例的方法(靜態方法)。

比如 Hibernate 的 SessionFactory,它充當數據存儲源的代理,並負責創建Session 對象。SessionFactory 並不是 輕量級的,一般情況下,一個項目通常只需要一個 SessionFactory 就夠,這是就會使用到單例模式。

2、單例設計模式八種方式

1. 餓漢式(靜態常量)
2.餓漢式(靜態代碼塊)
3.懶漢式(線程不安全)
4.懶漢式(線程安全,同步方法)
5.懶漢式(同步代碼塊)
6.雙重檢查
7.靜態內部類
8.枚舉

2.1 餓漢式(靜態常量)

步驟如下:

  1. 構造器私有化 (防止 new )
  2. 類的內部創建對象
  3. 向外暴露一個靜態的公共方法。getInstance
  4. 代碼實現
/**
 * 單例模式之 餓漢式(靜態變量)
 * 缺點,可能並沒有用到這個類,浪費內存。
 */
public class Singleton {

    private Singleton(){

    }

    private final static Singleton singleton = new Singleton();


    public static Singleton getInstance() {
        return singleton;
    }

}

優缺點說明:

  1. 優點:這種寫法比較簡單,就是在類裝載的時候就完成實例化。避免了線程同步問題。
  2. 缺點:在類裝載的時候就完成實例化,沒有達到LazyLoading的效果。如果從始至終從未使用過這個實例,則
    會造成內存的浪費
  3. 這種方式基於classloder機制避免了多線程的同步問題,不過,instance在類裝載時就實例化,在單例模式中大 多數都是調用 getInstance 方法,但是導致類裝載的原因有很多種,因此不能確定有其他的方式(或者其他的靜 態方法)導致類裝載,這時候初始化 instance 就沒有達到 lazy loading 的效果
  4. 結論:這種單例模式可用,可能造成內存浪費。
2.2 餓漢式(靜態代碼塊)
/**
 * 單例模式之 餓漢式(靜態代碼塊)
 * 缺點,可能並沒有用到這個類,浪費內存。
 */
public class Singleton {
    private  static Singleton singleton;

    private Singleton(){

    }

    static {
        singleton = new Singleton();
    }


    public static Singleton getInstance() {
        return singleton;
    }
}

這種方式和第一種方式類似,只不過將類實例化的過程放在了靜態代碼塊中,也是在類裝載的時候,就執 行靜態代碼塊中的代碼,初始化類的實例。優缺點和上面是一樣的。

2.3 懶漢式(線程不安全)
/**
 * 單例模式之 懶漢式(靜態方法)
 * 缺點,存在線程安全問題,不推薦使用
 */
public class Singleton {
    private  static Singleton singleton;

    private Singleton(){

    }

    
    //當處於多線程的情況下,幾個線程同時進來,那就失去了單例的效用了。
    public static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

    
}
2.4 懶漢式(線程安全,同步方法)
/**
 * 單例模式之 懶漢式(加鎖)
 * 缺點,結局了線程安全的問題,但是每次調用都要進行一次同步判斷,很影響效率,不推薦!
 */
public class Singleton {
    private  static Singleton singleton;

    private Singleton(){

    }
    
    public static synchronized Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

}
2.5 懶漢式(同步代碼塊)
/**
 * 單例模式之 懶漢式(代碼塊加鎖)
 * 這種方式並沒有起到任何的效果,多線程情況下還是會存在多個對象的情況。
 */
public class Singleton {
    private  static Singleton singleton;

    private Singleton(){

    }

     /**
      * 當線程a, b, c進來的時候,singleton都是null,所以都進到判斷裏面。
      * 由於加了鎖,所以a先進去實例對象後,b和c在外面等着,a出去以後鎖釋放,b和c先後又繼續實例對象了。
      * 所以這種方式並不能做到單例!!!
      **/
    public static  Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }

}

2.6 雙重檢查
/**
 * 單例模式之 懶漢式(雙重檢查)
 * 這種方式推薦使用的。
 */
public class Singleton {
    private  static volatile Singleton singleton;  // volatile 數據修改後做一個即時的更新

    private Singleton(){

    }


     /**
      * 當線程a, b, c進來的時候,singleton都是null,所以都進到判斷裏面。
      * 由於加了鎖,所以a先進去實例對象後,b和c在外面等着。
      * a出去以後鎖釋放,b和c先後進去,但是因爲此時singleton已經不是null了,所以直接出去了。
      * 而下次再次調用這個方法的時候,在第一重判斷就被攔住了,不用進行同步,又保證了效率。
      **/
    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

2.7 靜態內部類

/**
 * 單例模式之 靜態內部類
 * 好處:
 * 1、當一個類在加載的時候,靜態內部類是不會被立即加載的。
 * 2、類在加載的過程中是線程安全的。
 * 這種方式推薦使用的。
 */
public class Singleton {


    private Singleton(){

    }

    private static class InnerSingleton {
        private static final Singleton INSTANCE = new Singleton();
    }


    /**
    * 這時候纔去初始化靜態內部類
    **/
    public static Singleton getInstance() {
        return InnerSingleton.INSTANCE;
    }

}
2.8 枚舉
/**
 * 單例模式之 枚舉
 * 避免了多線程同步問題
 * 這種方式推薦使用的。
 */
public class   Singleton {
    
    public static void main(String[] args) {
        SingletonEnum singletonEnum = SingletonEnum.INSTANCE;
        SingletonEnum singletonEnum2 = SingletonEnum.INSTANCE;
//        singletonEnum.sayHello();

        System.out.println(singletonEnum == singletonEnum2); //true 表示兩個對象相同
    }

}

enum SingletonEnum {
    INSTANCE;

    public void sayHello() {
        System.out.println("hello~");
    }
}

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