java基礎_設計模式_單例模式

剛剛接觸單例模式的我們,在閱讀大神寫的代碼的時候,有時很想不通爲什麼這裏要使用單例這種模式,有啥好處嗎,不這樣寫又會咋滴等問題的困擾。下面我就想比較通俗的、用自己的語言組織講解一下單例模式,要是有地方理解不到位或出現偏差,希望大家能及時指出。


1.什麼是單例模式?


2.爲什麼會有這種需求,在哪些地方用單例模式,原因或者好處是什麼?


3.如何創建單例模式?常見的創建方式優缺點。


4.單列和工具類很像,比如math類是這樣使用的Math.double(2,8);有啥區別呢?


1.單例模式:

保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。

通常我們可以讓一個全局變量使得一個對象被訪問,但它不能防止你實例化多個對象。一個最好的辦法就是,讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例可以被創建,並且它可以提供一個訪問該實例的方法。


簡單的講就是:整個項目,有且只能創建唯一一個這個類的實例。 


2.爲啥會有這樣的需求呢?舉個例子在電腦任務欄右鍵然後點擊啓動任務管理器,會彈出一個任務管理器窗口,然後你再重複這樣的操作,看能再彈出一個任務管理器的窗口嗎?肯定不能!要是誰能,請私信我。哈哈哈。。。這裏對彈出窗口唯一限制用的就是單列模式。有且只能創建唯一一個實例即確保對象的唯一性。

爲了確保對象的唯一性我們可以使用單列,但是爲什麼要確保對象的唯一性?

我認爲好處有兩點:

①省資源。項目中大量使用一個類,比如網絡請求框架,要是每次使用創建對象都new的話,很浪費資源的。

比如我封裝的網絡框架使用時是這樣的:ApiService.getInstance().getData().enqueue(//請求回調操作);

②就像彈出任務管理器一樣要統一,只能有一個。


3.創建單列的方式:

最簡單的就是根據單列的概念去創建。即有且只有一個對象。①私有的構造方法,不允許外部代碼通過new的方式創建對象。②獲取對象的方法中加上是否已經存在的判斷,有則直接返回,沒有則new一個。

如下:

/**
 * 單利模式總結
 * 簡單的講單列要達到什麼效果?整個項目中 有且只能產生一個類的實例
 * 我認爲使用單列模式原因主要有以下兩點
 * 1.減少內存的佔用 要是在每一次使用的時候都new一個對象,相當佔用資源
 * 2.有些情況適合使用單列 比如桌面上彈出任務管理器窗口 是隻能彈出一個的
 * 3.如何使用單列?
 */

public final class Singleton {

    private static Singleton singleton;

    //1.私有的構造 要是修飾public或者不寫的話 類創建對象的時候直接走默認的構造 private意味着對於外部代碼而言不能通過new來實例化 但是類內部可以的
    private Singleton() {

    }

    //2.通過Singleton.getInstance() 獲取唯一實例 加上判斷 存在則返回不存在則直接本類中new 這是最簡單的創建單例方式
    public static Singleton getInstance() {

        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

}

測試類:

/**
 * 測試單例
 */

public class TestSingleTon {
    public static void main(String[] args) {

        //        Singleton singleton = new Singleton();  提示錯誤:私有的構造不允許new
        Singleton instance1 = Singleton.getInstance();
        Singleton instance2 = Singleton.getInstance();

        if (instance1 == instance2) {
            System.out.println("說明是單例模式,只能創建一個實例");
        }
    }
}
說明創建了唯一的一個實例。

但是存在一個問題就是,當有多個線程並行調用getInstance()的時候,就會創建多個單列。也就是說在多線程下不能正常工作。最直接的方法就是加鎖,只能一個一個的來,代碼如下:

  public static Singleton getInstance() {.... }  改爲  public synchronizeed  static Singleton getInstance() {...}

這樣保證了同一時間只能有一個線程調用getInstance()方法。但是效率極低,很少有情況會使用同步啊。(關於同步和異步的概念,在文章末尾有簡單的解釋)

爲了保證多線程訪問我們一般會雙重加鎖:

雙重檢查鎖:double-checked-locking   改變了3,4,5行代碼

public final class Singleton {

    private static Singleton singleton;

    //1.私有的構造  要是修飾public或者不寫的話 類創建對象的時候直接走默認的構造 private意味着對於外部代碼而言不能通過new來實例化 但是類內部可以的
    private Singleton() {

    }

    //2.通過Singleton.getInstance() 獲取唯一實例 加上判斷 存在則返回不存在則直接本類中new  這是最簡單的創建單例方式
    public synchronized static Singleton getInstance() {

        if (singleton == null) {                    //3.多個線程一起進入同步代碼塊的if
            synchronized (Singleton.class) {        //4.修飾一個代碼塊,被修飾的代碼塊稱爲同步語句塊,其作用的範圍是大括號{}括起來的代碼,作用的對象是調用這個代碼塊的對象;
                if (singleton == null) {            //5.保證進入一個 判斷是不是有了 有則返回
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

}

這樣就解決了多線程訪問單列的問題。


4.對比工具類:

給大家放一個常用的Math類的源代碼:

以下是Math類:
public final class Math {

    /**
     * Don't let anyone instantiate this class.
     */
    private Math() {}

    public static native double sin(double a);

}
簡單分析:

①私有的構造不允許外部代碼以new的方式創建對象

②聲明爲final關鍵字不允許被其他類繼承

③提供native修飾的靜態方法,方法本身沒有實現,意味着這樣的計算是使用jni的方式,預計c的計算效率高。
     注:Native Method 就是一個java調用非java代碼的接口。該方法沒有具體的java語言實現,比如又可能是c。

④自己書寫工具類的注意final關鍵字等這樣的細節,儘量規範。

⑤哇塞,突然感覺好心酸啊。工具類竟然是一個不能new對象、不能有子類(final),得多麼的寂寞。


以上就是自己對單例模式的理解,有問題請大家積極指出。


PS:線程同步和異步的概念

什麼時候必須同步?什麼叫同步?如何同步?

只要在幾個線程之間共享變量,就必須使用synchronized同步(或者volatite易變的)確保一個線程可以看見另一個線程做的更改。一個線程所做的變化何時以及如何變成對其他線程可見。

同步:共享的資源在同一時刻只能被一個線程使用,這種方式成爲同步。

爲了防止多個線程併發對同一數據的修改,所以需要同步,否則會造成數據不一致,也就是所謂的線程安全。

同步:提交請求=》等待服務器處理=》處理完返回  這個期間客戶端瀏覽器不能幹任何事
異步:請求通過事件觸發=》服務器處理(此時瀏覽器依然可以做其他事情)=》處理完畢。

簡單的講同步即有順序
異步:大家一起上公家車,沒有秩序,可以同時發生。

很多時候我們使用的是異步多線程來處理同一業務裏的大量數據,好比一萬個訂單要處理,如果你使用一個線程順序執行,一個個處理,非常耗時間;但是多線程就可以開100個線程異步處理,這樣效率提高很多。

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