Java設計模式系列-(Singleton)單例模式

點擊上方“框架師”,選擇“置頂公衆號

我們一起學習進步!

正文

單例模式

單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。

這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。

簡單來說就是保證在內存種只有一個實例!!!

應用場景

  • Windows時多進程多線程的,在操作一個文件的時候,就不可避免地出現多個進程或線程同時操作一個文件的現象,所以所有文件的處理必須通過唯一的實例來進行。

優點:

  • 在內存裏只有一個實例,減少了內存的開銷,尤其是頻繁的創建和銷燬實例

  • 避免對資源的多重佔用(比如寫文件操作)。

缺點:

  • 沒有接口,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。

  • 使用場景:

    • 要求生產唯一序列號。

    • WEB 中的計數器,不用每次刷新都在數據庫里加一次,用單例先緩存起來。

    • 創建的一個對象需要消耗的資源過多,比如 I/O 與數據庫的連接等。

單例模式的幾種實現

  • 第一種

/**
 * Software:IntelliJ IDEA 2018.2.4 x64
 * Author: MoBai·傑
 * Date: 2020/5/12 9:28
 * ClassName:Mgr01
 * 類描述:餓漢式
 * 類加載到內存後,就實例化一個單例,JVM保證線程安全
 * 優點:簡單實用,推薦使用
 * 缺點:不管用不用,類加載時都會完成實例化
 */

public class Mgr01 {

    // 創建一個Mgr01的對象
    private static final Mgr01 INSTANCE = new Mgr01();

    // 私有化構造方法,不讓該類實例化
    private Mgr01() {

    }

    // 獲取INSTANCE對象
    public static Mgr01 getInstance() {
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    // 主方法
    public static void main(String[] args) {
      //不合法的構造函數
      //編譯時錯誤:構造函數 Mgr01() 是不可見的
      //Mgr01 m1 = new Mgr01();
        Mgr01 m1 = Mgr01.getInstance();
        Mgr01 m2 = Mgr01.getInstance();
        System.out.println(m1.equals(m2));
    }
}
  • 第二種:

/**
 * Software:IntelliJ IDEA 2018.2.4 x64
 * Author: MoBai·傑
 * Date: 2020/5/12 9:42
 * ClassName:Mgr02
 * 類描述:餓漢式
 * 使用靜態代碼塊初始化
 */

public class Mgr02 {

    private static final Mgr02 INSTANCE;

    // 使用靜態代碼塊初始化
    static {
        INSTANCE = new Mgr02();
    }

    private Mgr02() {

    }

    public static Mgr02 getInstance() {
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        Mgr02 m1 = Mgr02.getInstance();
        Mgr02 m2 = Mgr02.getInstance();
        System.out.println(m1.equals(m2));
    }
}


這種其實和第一種實現方式是一樣的,區別在於使用了靜態代碼塊初始化

  • 第三種:

/**
 * Software:IntelliJ IDEA 2018.2.4 x64
 * Author: MoBai·傑
 * Date: 2020/5/12 9:46
 * ClassName:Mgr03
 * 類描述:懶漢式
 * 優點: 可以按需調用
 * 缺點: 線程不安全
 */

public class Mgr03 {
    // 創建時不進行初始化
    private static Mgr03 INSTANCE;

    // 私有構造方法,不進行實例化
    private Mgr03() {
    }

    // 進行判斷,調用getInstance方法爲null就初始化
    public static Mgr03 getInstance() {
        // INSTANCE等於null就進行初始化
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 判斷INSTANCE不等於null就不進行初始化
            INSTANCE = new Mgr03();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            // JDK1.8新增的lambd
            new Thread(() ->
            // com.mobaijun.singleton.Mgr03@8ae2e7e
            // com.mobaijun.singleton.Mgr03@5372fc23
            // 調用getInstance()方法.打印內存地址,判斷結果是否一樣
            System.out.println(Mgr03.getInstance().hashCode())
            ).start();
        }
    }
}


  • 第四種:

/**
 * Software:IntelliJ IDEA 2018.2.4 x64
 * Author: MoBai·傑
 * Date: 2020/5/12 9:46
 * ClassName:Mgr04
 * 類描述:懶漢式
 * 優點: 可以按需調用
 * 缺點: 線程不安全
 * 解決方案: 添加synchronized
 * 新缺點: 添加synchronized後線程效率會降低
 */

public class Mgr04 {

    private static Mgr04 INSTANCE;

    private Mgr04() {
    }

    // 添加synchronized關鍵字
    public static synchronized Mgr04 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr04();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() ->
                    System.out.println(Mgr04.getInstance().hashCode())
            ).start();
        }
    }
}


  • 第五種:

/**
 * Software:IntelliJ IDEA 2018.2.4 x64
 * Author: MoBai·傑
 * Date: 2020/5/12 9:46
 * ClassName:Mgr05 
 * 類描述:懶漢式
 * 優點: 可以按需調用
 * 缺點: 線程不安全
 * 解決方案: 添加synchronized
 * 新缺點: 添加synchronized後線程效率會降低
 */

public class Mgr05 {

    private static Mgr05 INSTANCE;

    private Mgr05() {
    }

    public static synchronized Mgr05 getInstance() {
        if (INSTANCE == null) {
            // 試圖通過添加synchronized減小同步代碼塊的方式提高效率,然後不可行
            synchronized (Mgr05.class) {
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Mgr05();
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() ->
                    System.out.println(Mgr05.getInstance().hashCode())
            ).start();
        }
    }
}


  • 第六種:

/**
 * Software:IntelliJ IDEA 2018.2.4 x64
 * Author: MoBai·傑
 * Date: 2020/5/12 9:46
 * ClassName:Mgr06
 * 類描述:懶漢式
 * 優點: 可以按需調用
 * 缺點: 線程不安全
 * 解決方案: 添加synchronized
 * 新缺點: 添加synchronized後線程效率會降低
 */

public class Mgr06 {

    // volatile解決指令重排的問題
    private static volatile Mgr06 INSTANCE;

    private Mgr06() {
    }

    public static Mgr06 getInstance() {
        // 1.首先判斷是否爲null
        if (INSTANCE == null) {
            // 2.如果爲null即不執行
            // 雙重檢查
            synchronized (Mgr06.class) {
                if (INSTANCE == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Mgr06();
                }
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() ->
                    System.out.println(Mgr06.getInstance().hashCode())
            ).start();
        }
    }
}
  • 第七種:

/**
 * Software:IntelliJ IDEA 2018.2.4 x64
 * Author: MoBai·傑
 * Date: 2020/5/12 9:46
 * ClassName:Mgr07
 * 類描述:懶漢式
 * 最完美的寫法
 * 使用靜態內部類方式,JVM保證單例
 * 加載外部類時不會加載內部類,可以實現懶加載
 */

// 外部類
public class Mgr07 {


    // 私有構造方法
    private Mgr07() {
    }

    // 內部類
    private static class Mgr07Holder {
        private final static Mgr07 INSTANCE = new Mgr07();
    }

    // 調用Mgr07Holder.INSTANCE
    public static Mgr07 getInstance() {
        return Mgr07Holder.INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() ->
                    System.out.println(Mgr07.getInstance().hashCode())
            ).start();
        }
    }
}

這種方式在之前的單例模式寫法種是被認爲最完美的

  • 第八種:

/**
 * Software:IntelliJ IDEA 2018.2.4 x64
 * Author: MoBai·傑
 * Date: 2020/5/12 10:21
 * EnumName:Mgr08
 * 枚舉類描述:單例
 * 不僅可以解決線程同步,還可以防止反序列化
 */

public enum Mgr08 {

    // 只設置一個值
    INSTANCE;

    public void m() {

    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() ->
                    System.out.println(Mgr08.INSTANCE.hashCode())
            ).start();
        }
    }
}


據說枚舉單例是Java某一個創始人設計的,堪稱最完美,最簡單的單例啦,當然,使用單例的場景很多,我們還是需要根據現實開發的場景去確定使用哪種方式實現單例模式;

好了各位小夥伴們,以上就是本文的全部內容了。能看到這裏的都是最優秀的程序員,升職加薪就是你了👍。如果覺得不過癮,還想看到更多,我再給大家推薦幾篇。

日常操作來了!如果覺得這篇文章有點用的話,求在看、求轉發,明人不說暗話,我喜歡這種被大傢伙寵愛的感覺。

one more thing!如果大家想要第一時間看到墨白更新的文章,可以掃描下方的二維碼,關注我的公衆號。我們下篇文章見!

本文分享自微信公衆號 - 框架師(mohu121)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

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