談談java的單例模式吧

單例模式,顧名思義就是單個實例,就是說一個類有且僅有一個實例,應該是23中設計模式中最簡單也最好理解的設計模式了,同時簡單的往往也是重要的和常用的,對吧?簡單的往往也是常用的,這句話在任何地方都能適用

 

一、常見的幾種寫法

1、餓漢式(可以使用)

特點:線程安全的,不能實現延遲加載

public class HungryMan {


    /**1、私有化構造器:所有的單例模式第一步永遠都是私有化構造器,因爲只有不讓在外面調用構造器new 
     *  對象了,才能控制對象的數量,這是單例模式的前提
     */
    private HungryMan(){}

    //2、內的內部創建出實例
    private static final HungryMan HUNGRY_MAN = new HungryMan();

    //3、提供公共靜態的方法返回實例
    public static HungryMan getInstance(){
        return HUNGRY_MAN;
    }
}

1.1餓漢式之靜態代碼塊版(可以使用)

特點:線程安全的,不能實現延遲加載

public class HungryMan1 {

    private HungryMan1(){}

    private static HungryMan1 hungryMan1;

    static {
        hungryMan1 = new HungryMan1();
    }

    public static HungryMan1 getInstance(){
        return hungryMan1;
    }
    
}

2、懶漢式(不推薦使用)

特點:不是線程安全的,能延遲加載

public class LazyMan {

    private LazyMan(){}

    private static LazyMan lazyMan;

    public static LazyMan getInstance() {
        if (lazyMan == null) {
            lazyMan = new LazyMan();
        }
        return lazyMan;
    }
}

2.1懶漢式之同步方法版(可以使用)

特點:線程安全的,能延遲加載,但效率低(當有多個線程同時調用時,不管有沒有實例都要等待拿鎖)

public class LazyMan1 {

    private LazyMan1(){}

    private static LazyMan1 lazyMan1;

    public static synchronized LazyMan1 getInstance(){
        if (lazyMan1 == null) {
            lazyMan1 = new LazyMan1();
        }
        return lazyMan1;
    }
}

2.2懶漢式之同步代碼塊版(可以使用)

特點:線程安全的,能延遲加載,但效率低

public class LazyMan2 {

    private LazyMan2(){}

    private static LazyMan2 lazyMan2;

    public static LazyMan2 getInstance(){
        synchronized (LazyMan2.class){
            if (lazyMan2 == null) {
                lazyMan2 = new LazyMan2();
            }
            return lazyMan2;
        }
    }
}

2.3懶漢式之雙重檢查版(較推薦使用)

特點:線程安全的,能延遲加載,效率高(據說雙重檢查有線程安全問題,不確定還)

public class LazyMan3 {

    private LazyMan3(){}

    private static LazyMan3 lazyMan3;

    public static LazyMan3 getInstance(){
        if (lazyMan3 == null){
           synchronized (LazyMan3.class){
              if (lazyMan3 == null) {
                 lazyMan3 = new LazyMan3();
               }
           }
        }
        return lazyMan3;
    }
}

3、靜態內部類版(推薦使用)

特點:線程安全,能延遲加載,效率高

public class StaticInsideClass {

    private StaticInsideClass(){}

    //在靜態內部類中創建外部類的實例
    //PS:靜態內部類會等到它被調用的時候才加載
    private static class InnerClass{
        private static final StaticInsideClass STATIC_INSIDE_CLASS = new StaticInsideClass();
    }

    public static StaticInsideClass getInstance(){
        return InnerClass.STATIC_INSIDE_CLASS;
    }
}

4、枚舉類版(推薦使用)

特點:線程安全的,效率高,能避免反射和反序列化創建對象的問題,不能延遲加載

public enum EnumSingleton {

      //被枚舉出來的對象
      Instance;



    /*當前枚舉類的其他屬性和方法*/
      //名字
      private String name;
      //年齡
      private Integer age;


    public void doSomeThing(){
        System.out.println("當前枚舉出來的對象做一些事情~~~");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

5、登記式:用一個線程安全的Map來保存對象實例。在此就不寫了,大家可以自己實現一下。

二、常見的應用場景

首先,想一下單例模式的特點:在一個系統中只有一個對象實例,我們可以好好設計這個類,比如設計一些讀取和寫入的方法,來對類中的屬性(比如集合等)進行操作,因爲就只有一個對象,該對象擁有自己的一套屬性和方法,即有自己的內存空間,這樣該類就能成爲系統運行期間的一個小的內存數據庫。(當前要保存該類不會被JVM當成垃圾回收了纔行)

1、系統配置文件的讀取時,對應一個單例的類。

2、網站計數器來記錄網站訪問的次數、在線人數等

3、臨時對象的頻繁創建和銷燬時,比如需要創建一個對象來保存數據,然後進行插入數據庫操作時。

4、各種池的創建都是單例模式的,比如數據庫連接池、線程池等。

總之一句話,凡是需要保證一份資源只能有一份時,就用單例模式。

三、問題

這裏我有個問題先記下來:爲什麼單例模式的對象不會被JVM當成垃圾回收?

答:  等我瞭解完JVM的知識後再來填坑吧

聰明的小夥伴看到後,能否在我填坑前,在評論區留言解答呢?說出你的看法,show time~~

 

 

 

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