設計模式-單例模式、多例模式

Or_One發表於2020-12-14

單例設計模式

正常情況下一個類可以建立多個物件

public static void main(String[] args) {
	// 正常情況下一個類可以建立多個物件
	Person p1 = new Person();
	Person p2 = new Person();
	Person p3 = new Person();
}

但是有些時候的某些類, 我們希望只能建立單一的一個物件, 這時候我們需要使用到單例設計模式, 下面我們來介紹一下單例設計模式.

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

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

單例設計模式的作用

  • 目的: 保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點
  • 解決的問題: 一個全域性使用的類頻繁地建立與銷燬。
  • 什麼時候使用: 當您想控制例項數目,節省系統資源的時候。

單例設計模式的實現步驟

  1. 構造方法私有化,使其不能在類的外部通過new關鍵字例項化該類物件。
  2. 在該類內部產生一個唯一的例項化物件,並且將其封裝為private static型別的成員變數
  3. 定義一個靜態方法返回這個唯一物件

餓漢式

餓漢單例設計模式就是使用類的時候已經將物件建立完畢,不管以後會不會使用到該例項化物件,先建立了再說。很著急的樣子,故被稱為“餓漢模式”。

程式碼如下:

public class Singleton {
    // 1.將構造方法私有化,使其不能在類的外部通過new關鍵字例項化該類物件。
    private Singleton() {}

    // 2.在該類內部產生一個唯一的例項化物件,並且將其封裝為private static型別的成員變數。
    private static final Singleton instance = new Singleton();
    
    // 3.定義一個靜態方法返回這個唯一物件。
    public static Singleton getInstance() {
        return instance;
    }
}

懶漢式

懶漢單例設計模式就是呼叫getInstance()方法時例項才被建立,先不急著例項化出物件,等要用的時候才例化出物件。不著急,故稱為“懶漢模式”。

懶漢式-普通方式【不能用】

(1) 實現步驟

  1. 私有構造方法
  2. 建立一個物件的宣告, 但是不例項化.
  3. 對外暴露一個用於獲取物件的靜態方法
    • 判斷物件是否為空
    • 如果為空就例項化
    • 返回當前物件

(2) 程式碼實現

package com.itheima._01single.demo02;
/**
 *
 * 懶漢式
 *  懶: 上來不著急建立物件, 什麼時候用, 什麼時候建立
 *
 */
public class Singleton {
    // 1. 私有構造
    private Singleton() {}
    // 2. 只宣告本類的引用, 後面不建立物件
    private static Singleton instance;
    // 3. 提供公共的用來獲取本類物件的方法
    public static Singleton getInstance() {
        // 判斷: 如果instance為null  => 建立物件
        if (instance == null) {
            // 為了看到效果, 所以讓執行緒睡一會
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 什麼時候用, 什麼時候建立
            instance = new Singleton();
        }
        // 如果instance不為null, 直接返回
        return instance;
    }
}

(3) 優缺點

  • 優點: 起到了懶載入的效果
  • 缺點: 多執行緒環境下會出現問題, 可能建立出多個例項, 不符合單例的原則.

結論: 在實際開發中不要使用這種方式.

懶漢式 - 同步方法【能用】

(1) 實現步驟

  1. 私有構造方法

  2. 類的內部建立物件

  3. 對外暴露一個用於獲取物件的靜態同步方法

    • 判斷物件是否為空

    • 如果為空就例項化

    • 返回當前物件

(2) 程式碼實現

package com.itheima._01single.demo03;

/**
 * 懶漢式 - 同步方法
 */
public class Singleton {
    // 1. 私有構造
    private Singleton() {
    }

    // 2. 宣告引用
    private static Singleton instance; // 例項

    // 3. 獲取物件的方法
    public static synchronized Singleton getInstance() {
        // 判斷, 如果為null, 建立物件
        if (instance == null) {

            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            instance = new Singleton();
        }
        // 如果不為null, 直接返回
        return instance;
    }


}

(3) 優缺點

優點: 解決了多執行緒中建立多個物件的問題

缺點: 效率太低, 每次執行都要進入同步方法.

總結: 實際開發中不推薦使用

懶漢式 - 同步程式碼塊【過渡】

(1) 實現步驟

  1. 私有構造方法

  2. 類的內部建立物件

  3. 對外暴露一個用於獲取物件的靜態方法

    • 判斷物件是否為空

    • 在同步程式碼塊中進行判斷, 如果為空就例項化

    • 返回當前物件

(2) 程式碼實現

package com.itheima._01single.demo04;

/**
 *
 * 懶漢式 - 同步程式碼塊
 *
 *  這種實現方式是有問題的, 我要用這種方式給大家引出最終的BOSS
 *
 *  剛才使用同步方法的時候, 可以保證只建立一個物件, 但是每一次都要先獲取鎖
 *
 *  1. 不每一次上來就直接先獲取鎖
 *  2. 等判斷完為null, 再獲取鎖, 建立物件
 *
 */
public class Singleton {
    // 1. 私有構造
    private Singleton() {}
    // 2. 宣告引用
    private static Singleton instance;
    // 3. 公共方法
    // 把獲取鎖物件的時機延後, 且不滿足條不會獲取鎖(減少了獲取鎖的次數)
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                instance = new Singleton();
            }
        }
        return instance;
    }
}

(3) 優缺點

  • 缺點: 想提高同步方法的效率, 但是又出現了多執行緒中建立多個物件的問題

實際開發中不能使用

雙重檢查(Double-Check)【推薦使用】

(1) 實現步驟

  1. 私有構造方法
  2. 類的內部建立物件(使用volatile修飾)
  3. 對外暴露一個用於獲取物件的靜態方法
    • 判斷物件是否為空
    • 同步程式碼塊
    • 在同步程式碼塊中再次判斷物件是否為空
    • 如果為空再例項化
    • 最後返回當前物件

(2) 程式碼實現

package com.itheima._01single.demo05;

/**
 *
 *  雙重檢查
 *
 *  1. 兩個if(instance == null) 的判斷
 *  2. 加上volatile關鍵字, 保證共享資料可可見性
 *
 */
public class Singleton {
    // 1. 私有構造
    private Singleton() {}

    // 2. 宣告引用
    private static volatile Singleton instance;

    // 3. 提供獲取物件的方法
    public static Singleton getInstance() {
            // 如果物件不存在
            if (instance == null) {

                // 執行緒1, 判斷是null ||  執行緒2, 判斷是null
                // 同步鎖
                // 問題: 多執行緒中有可能建立多個物件
                synchronized (Singleton.class) {
                    if (instance == null) {
                        // 建立物件
                        instance = new Singleton();
                    }
                }
            }

            return instance;
    }
}

(3) 優缺點

  • 進行兩次null檢查, 可以保證執行緒安全
  • 例項化的程式碼只會執行一次, 後面再次訪問的時候直接return例項化的物件, 避免了同步的多次執行.
  • 執行緒安全, 懶載入, 效率較高

總結: 實際開發中, 推薦使用這種單例設計模式

雙重檢查單例的圖解

在這裡插入圖片描述

多例設計模式

多例模式,是一種常用的軟體設計模式。通過多例模式可以保證系統中,應用該模式的類有固定數量的例項。多例類要自我建立並管理自己的例項,還要向外界提供獲取本類例項的方法。

實現步驟

​ 1.建立一個類, 將構造方法私有化,使其不能在類的外部通過new關鍵字例項化該類物件。

​ 2.在類中定義該類被建立的總數量

​ 3.在類中定義存放類例項的list集合

​ 4.在類中提供靜態程式碼塊,在靜態程式碼塊中建立類的例項

​ 5.提供獲取類例項的靜態方法

實現程式碼如下

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Multition {
    // 定義該類被建立的總數量
    private static final int maxCount = 3;
    // 定義存放類例項的list集合
    private static List instanceList = new ArrayList();
    // 構造方法私有化,不允許外界建立本類物件
    private Multition() {
    }
    static {
        // 建立本類的多個例項,並存放到list集合中
        for (int i = 0; i < maxCount; i++) {
            Multition multition = new Multition();
            instanceList.add(multition);
        }
    }
    // 給外界提供一個獲取類物件的方法
    public static Multition getMultition(){
        Random random = new Random();
        // 生成一個隨機數
        int i = random.nextInt(maxCount);
        // 從list集合中隨機取出一個進行使用
        return (Multition)instanceList.get(i);
    }
}

測試結果

public static void main(String[] args) {
    // 編寫一個迴圈從中獲取類物件
    for (int i = 0; i < 10; i++) {
        Multition multition = Multition.getMultition();
        System.out.println(multition);
    }
}

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-cdULCYPu-1607883570994)(imgs/1565598708749.png)]

多例模式可以保證系統中一個類有固定個數的例項, 在實現需求的基礎上, 能夠提高例項的複用性.

相關文章