23天設計模式之單例模式

23天設計模式之單例模式

文章簡介

《23天設計模式之單例模式》這是個人第二篇博客。在接下來的23天內,咱們將23種設計模式都去了解一下。今天咱們就來學習最簡單的單例模式java

在學習設計模式以前咱們不可避免要去想爲何要學習這個東西,它是用來幹嗎的?算法

  • 要知道在軟件開發中,要實現可維護、可擴展,就必須儘可能複用代碼,而且下降代碼的耦合度
  • 另外,我認爲學習設計模式能夠潛移默化地對咱們的編程思想產生好的影響。其實咱們常常都有使用到設計模式,只是沒有去學習過便沒有意識到,好比JDK的動態代理就是一種代理模式
  • 設計模式主要是基於OOP編程【面向對象編程(object-oriented programming);】提煉的,它基於如下幾個原則。

OOP7大原則

  • 開閉原則。對擴展開放,對修改關閉。
  • 里氏替換原則。繼承必須確保超類所擁有的性質在子類中仍然成立。(子類在程序中能夠替換父類)
  • 依賴倒置原則。要面向接口編程,不要面向實現編程。(實現公共接口,而不是每一個接口都實現)
  • 單一職責原則。控制類的粒度大小、將對象解耦、提升其內聚性。(方法的原子性)
  • 接口隔離原則。要爲各個類創建他們所須要的專用接口。
  • 迪米特法則。只與你的直接朋友交談,不跟「陌生人」說話。
  • 合成複用原則。儘可能先使用組合或聚合等關聯關係來實現,其次才考慮使用繼承關係來實現。

設計模式分類

整體分爲3大類:編程

  • 建立型模式:關注點是如何建立對象,其核心思想是要把對象的建立和使用相分離,這樣使得二者能相對獨立地變換。
    • 工廠方法:Factory Method
    • 抽象工廠:Abstract Factory
    • 建造者:Builder
    • 原型:Prototype
    • 單例:Singleton
  • 結構型模式。把類或對象結合在一塊兒造成一個更大的結構。
    • 適配器
    • 橋接
    • 組合
    • 裝飾器
    • 外觀
    • 享元
    • 代理
  • 行爲型模式。類和對象如何交互,及劃分責任和算法。
    • 責任鏈
    • 命令
    • 解釋器
    • 迭代器
    • 中介
    • 備忘錄
    • 觀察者
    • 狀態
    • 策略
    • 模板方法
    • 訪問者

單例模式

  • 單例模式指在一個進程中,某個類有且僅有一個實例。
  • 爲了防止調用者調用new方法繼續構造,構造方法要設置爲private。
  • 調用者要怎麼獲取到單例呢?咱們提供一個public static修飾的靜態方法給用戶返回單例。
  • 單例模式也分爲如下幾種,在下面詳細介紹。

餓漢式單例

  • 所謂餓漢式單例是指,不論是否調用獲取實例的方法,都會產生一個單例。直接上代碼!
  • 缺點:無論用不用都會產生單例,浪費空間。
  • 也稱爲靜態單例模式。
public class Singleton {

    //靜態單例
    private static final Singleton INSTANCE = new Singleton();

    //私有的構造方法
    private Singleton(){};

    // 獲取單例的靜態方法
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

懶漢式單例

  • 所謂懶漢是指,這個單例很懶,只有在調用方調用時,纔去生成單例。
  • 若是在多線程下不加鎖的話,會屢次調用構造方法建立多個實例。直接上代碼。
public class Singleton {

    //調用getInstance()方法時初始化
    private static Singleton instance;

    //輸出文字看這個構造方法被調用了幾回
    private Singleton(){
        System.out.println(Thread.currentThread().getName() + "調用了構造方法");
    };

    //加了判空
    public static Singleton getInstance(){
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    //使用多線程測試構造方法被調用了幾回。正常來講應該只調用一次。
    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            new Thread(Singleton::getInstance).start();
        }
    }
}

//輸出以下:
Thread-1調用了構造方法
Thread-2調用了構造方法
Thread-0調用了構造方法

分析:windows

  • 每次運行程序,輸出都不同;構造方法的明顯被屢次調用,即建立了多個單例。

DCL懶漢式單例

  • DCL是指雙重檢鎖。double-checked locking
public class Singleton {

    //volatile禁止指令重排優化
    private static volatile Singleton instance;

    private Singleton(){
        System.out.println(Thread.currentThread().getName() + "調用了構造方法");
    };

    //DCL雙重檢鎖
    public static Singleton getInstance(){
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            new Thread(Singleton::getInstance).start();
        }
    }
}

//輸出:
Thread-0調用了構造方法

分析:設計模式

  • 不管執行多少次main函數,始終只有一條線程能調用構造函數。安全

  • 指令重排:編譯器、JVM 或者 CPU 都有可能出於優化等目的,對於實際指令執行的順序進行調整。多線程

  • 爲何要禁止指令重排:多線程環境中線程交替執行,因爲編譯器指令重排的存在,兩個線程使用的變量可否保證一致性是沒法確認的,結果沒法預測。函數

靜態內部類實現單例

public class Singleton {

    private Singleton(){
    };

    public static Singleton getInstance() {
        return InnerClass.INSTANCE;
    }

    //靜態內部類
    public static class InnerClass {
        private static final Singleton INSTANCE = new Singleton();
    }
}

枚舉類實現單例

  • 正所謂道高一尺,魔高一丈。即便咱們私有了構造器,單例也並不安全。萬能的反射能夠仍調用私有構造器,所以咱們須要枚舉單例
  • 枚舉單例不會被反射破壞。
public enum Singleton {

    INSTANCE;

    public Singleton getInstance(){
        return INSTANCE;
    }
}

以上就是單例模式的主要內容。接下來咱們瞭解如下單例模式的常見應用場景。oop

單例模式的應用場景

  • Windows的Task Manager(任務管理器)就是很典型的單例模式學習

  • windows的Recycle Bin(回收站)就是典型的單例應用。在整個系統運行過程當中,回收站一直維護着僅有的一個實例。

  • 網站的計數器,通常也是採用單例模式實現,不然難以同步。

  • 多線程的線程池的設計通常也是採用單例模式,這是因爲線程池要方便對池中的線程進行控制。

以上

感謝您花時間閱讀個人博客,以上就是我對單例模式的一些理解,如有不對之處,還望指正,期待與您交流。

本篇博文系原創,僅用於我的學習,轉載請註明出處。

相關文章
相關標籤/搜索