日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

《一天聊一个设计模式》 单例

發布時間:2023/12/13 asp.net 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《一天聊一个设计模式》 单例 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我是兔兔rabbit,關注我吧,給自己每天的進步找一個機會和理由,不要被無效信息淹沒

單例(Singleton)

Intent

確保一個類只有一個實例,并提供該實例的全局訪問點。

Class Diagram

使用一個私有構造函數、一個私有靜態變量以及一個公有靜態函數來實現。

私有構造函數保證了不能通過構造函數來創建對象實例,只能通過公有靜態函數返回唯一的私有靜態變量。

Implementation

Ⅰ 懶漢式-線程不安全

以下實現中,私有靜態變量 uniqueInstance 被延遲實例化,這樣做的好處是,如果沒有用到該類,那么就不會實例化 uniqueInstance,從而節約資源。

這個實現在多線程環境下是不安全的,如果多個線程能夠同時進入 if (uniqueInstance == null) ,并且此時 uniqueInstance 為 null,那么會有多個線程執行 uniqueInstance = new Singleton(); 語句,這將導致實例化多次 uniqueInstance。

public class Singleton {private static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance;} }

Ⅱ 餓漢式-線程安全

線程不安全問題主要是由于 uniqueInstance 被實例化多次,采取直接實例化 uniqueInstance 的方式就不會產生線程不安全問題。

但是直接實例化的方式也丟失了延遲實例化帶來的節約資源的好處。

private static Singleton uniqueInstance = new Singleton();

Ⅲ 懶漢式-線程安全

只需要對 getUniqueInstance() 方法加鎖,那么在一個時間點只能有一個線程能夠進入該方法,從而避免了實例化多次 uniqueInstance。

但是當一個線程進入該方法之后,其它試圖進入該方法的線程都必須等待,即使 uniqueInstance 已經被實例化了。這會讓線程阻塞時間過長,因此該方法有性能問題,不推薦使用。

public static synchronized Singleton getUniqueInstance() {if (uniqueInstance == null) {uniqueInstance = new Singleton();}return uniqueInstance; }

Ⅳ 雙重校驗鎖-線程安全

uniqueInstance 只需要被實例化一次,之后就可以直接使用了。加鎖操作只需要對實例化那部分的代碼進行,只有當 uniqueInstance 沒有被實例化時,才需要進行加鎖。

雙重校驗鎖先判斷 uniqueInstance 是否已經被實例化,如果沒有被實例化,那么才對實例化語句進行加鎖。

public class Singleton {private volatile static Singleton uniqueInstance;private Singleton() {}public static Singleton getUniqueInstance() {if (uniqueInstance == null) {synchronized (Singleton.class) {if (uniqueInstance == null) {uniqueInstance = new Singleton();}}}return uniqueInstance;} }

考慮下面的實現,也就是只使用了一個 if 語句。在 uniqueInstance == null 的情況下,如果兩個線程都執行了 if 語句,那么兩個線程都會進入 if 語句塊內。雖然在 if 語句塊內有加鎖操作,但是兩個線程都會執行 uniqueInstance = new Singleton(); 這條語句,只是先后的問題,那么就會進行兩次實例化。因此必須使用雙重校驗鎖,也就是需要使用兩個 if 語句:第一個 if 語句用來避免 uniqueInstance 已經被實例化之后的加鎖操作,而第二個 if 語句進行了加鎖,所以只能有一個線程進入,就不會出現 uniqueInstance == null 時兩個線程同時進行實例化操作。

if (uniqueInstance == null) {synchronized (Singleton.class) {uniqueInstance = new Singleton();} }

uniqueInstance 采用 volatile 關鍵字修飾也是很有必要的, uniqueInstance = new Singleton(); 這段代碼其實是分為三步執行:

  • 為 uniqueInstance 分配內存空間
  • 初始化 uniqueInstance
  • 將 uniqueInstance 指向分配的內存地址
  • 但是由于 JVM 具有指令重排的特性,執行順序有可能變成 1>3>2。指令重排在單線程環境下不會出現問題,但是在多線程環境下會導致一個線程獲得還沒有初始化的實例。例如,線程 T1 執行了 1 和 3,此時 T2 調用 getUniqueInstance() 后發現 uniqueInstance 不為空,因此返回 uniqueInstance,但此時 uniqueInstance 還未被初始化。

    使用 volatile 可以禁止 JVM 的指令重排,保證在多線程環境下也能正常運行。

    Ⅴ 靜態內部類實現

    當 Singleton 類被加載時,靜態內部類 SingletonHolder 沒有被加載進內存。只有當調用 getUniqueInstance() 方法從而觸發 SingletonHolder.INSTANCE 時 SingletonHolder 才會被加載,此時初始化 INSTANCE 實例,并且 JVM 能確保 INSTANCE 只被實例化一次。

    這種方式不僅具有延遲初始化的好處,而且由 JVM 提供了對線程安全的支持。

    public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getUniqueInstance() {return SingletonHolder.INSTANCE;} }

    Ⅵ 枚舉實現

    public enum Singleton {INSTANCE;private String objName;public String getObjName() {return objName;}public void setObjName(String objName) {this.objName = objName;}public static void main(String[] args) {// 單例測試Singleton firstSingleton = Singleton.INSTANCE;firstSingleton.setObjName("firstName");System.out.println(firstSingleton.getObjName());Singleton secondSingleton = Singleton.INSTANCE;secondSingleton.setObjName("secondName");System.out.println(firstSingleton.getObjName());System.out.println(secondSingleton.getObjName());// 反射獲取實例測試try {Singleton[] enumConstants = Singleton.class.getEnumConstants();for (Singleton enumConstant : enumConstants) {System.out.println(enumConstant.getObjName());}} catch (Exception e) {e.printStackTrace();}} } firstName secondName secondName secondName

    該實現可以防止反射攻擊。在其它實現中,通過 setAccessible() 方法可以將私有構造函數的訪問級別設置為 public,然后調用構造函數從而實例化對象,如果要防止這種攻擊,需要在構造函數中添加防止多次實例化的代碼。該實現是由 JVM 保證只會實例化一次,因此不會出現上述的反射攻擊。

    該實現在多次序列化和序列化之后,不會得到多個實例。而其它實現需要使用 transient 修飾所有字段,并且實現序列化和反序列化的方法。

    Examples

    • Logger Classes
    • Configuration Classes
    • Accesing resources in shared mode
    • Factories implemented as Singletons

    JDK

    • java.lang.Runtime#getRuntime()
    • java.awt.Desktop#getDesktop()
    • [java.lang.System#getSecurityManager()](

    總結

    以上是生活随笔為你收集整理的《一天聊一个设计模式》 单例的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。