Java常用設計模式之單例模式

一:什麼是單例模式

單例模式定義很簡單:一個類中能創建一個實例,所以稱之爲單例!

那我們什麼時候會用到單例模式呢??

  • 那我們想想既然一個類中只能創建一個實例了,那麼可以說這是跟類的狀態與對象無關的了。

  • 頻繁創建對象、管理對象是一件耗費資源的事,我們只需要創建一個對象來用就足夠了!

學過Java Web的同學可能就知道:

  • Servlet是單例的

  • Struts2是多例的

  • SpringMVC是單例的

那既然多例是頻繁創建對象、需要管理對象的,那Struts2爲什麼要多例呢??

  • 主要由於設計層面上的問題,Struts2是基於Filter攔截類的,ognl引擎對變量是注入的。所以它要設計成多例的~

能使用一個對象來做就不用實例化多個對象!這就能減少我們空間和內存的開銷~

那有可能有的人又會想了:我們使用靜態類.doSomething()和使用單例對象調用方法的效果是一樣的啊。

  • 沒錯,效果就是一樣的。使用靜態類.doSomething()體現的是基於對象,而使用單例設計模式體現的是面向對象

二:代碼演示

編寫單例模式的代碼其實很簡單,就分了三步:

  • 將構造函數私有化

  • 在類的內部創建實例

  • 提供獲取唯一實例的方法

1:餓漢式

public class Single {

    private Single(){};

    private static Single single=new Single();

    public static Single getSingle(){
    
        return single;
    }


}

這種代碼我們稱之爲:“餓漢式”:

  • 一上來就創建對象了,如果該實例從始至終都沒被使用過,則會造成內存浪費

2:簡單懶漢式

既然說一上來就創建對象,如果沒有用過會造成內存浪費:

  • 那麼我們就設計用到的時候再創建對象

public class Single {

    // 1.將構造函數私有化,不可以通過new的方式來創建對象
    private Single(){}

    // 2.1先不創建對象,等用到的時候再創建
    private static Single single= null;

    // 2.1調用到這個方法了,證明是要被用到的了
    public static Single getSingle() {

        // 3. 如果這個對象引用爲null,我們就創建並返回出去
        if (single== null) {
            single= new Single();
        }

        return single;
    }
}

上面的代碼行不行??在單線程環境下是行的,在多線程環境下就不行了

  • 要解決也很簡單,我們只要加鎖就行

public class Single {

    private Single(){};

    private static Single single=null;

    public static synchronized Single getSingle(){
        if(single==null){
              single=new Single();
           
        }
        return single;
    }


}

上面面那種直接在方法上加鎖的方式其實不夠好,因爲在方法上加了內置鎖在多線程環境下性能會比較低下,所以我們可以將鎖的範圍縮小

public class Single {

    private Single(){};

    private static Single single=null;

    public static Single getSingle(){
        if(single==null){
            synchronized (Single.class){
                single=new Single();
            }
        }
        return single;
    }

}

那上面的代碼可行嗎??不行,因爲雖然加了鎖,但還是有可能創建出兩個對象出來的:

  • 線程A和線程B同時調用getJava3y()方法,他們同時判斷java==null,得出的結果都是爲null,所以進入了if代碼塊了

  • 此時線程A得到CPU的控制權-->進入同步代碼塊-->創建對象-->返回對象

  • 線程A完成了以後,此時線程B得到了CPU的控制權。同樣是-->進入同步代碼塊-->創建對象-->返回對象

  • 很明顯的是:Java3y類返回了不止一個實例!所以上面的代碼是不行的!

厲害的程序員又想到了:進入同步代碼塊時再判斷一下對象是否存在就穩了吧

public class Single {

    private Single(){};

    private static Single single=null;

    public static Single getSingle(){
        if(single==null){
            synchronized (Single.class){
                if(single==null){
                    single=new Single();
                }
                
            }
        }
        return single;
    }


}

其實還不穩!這裏會有重排序的問題

那麼接下來怎麼辦:要解決也十分簡單,加上我們的volatile關鍵字就可以了,volatile有內存屏障的功能

public class Single {

    private Single(){};

    private static volatile  Single single=null;

    public static Single getSingle(){
        if(single==null){
            synchronized (Single.class){
                if(single==null){
                    single=new Single();
                }

            }
        }
        return single;
    }


}

 

詳細的可查看:Java設計模式全解

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