你與面試官所瞭解的單例模式並不一樣!

来源:https://www.cnblogs.com/ziph/archive/2020/06/06/13056123.html
-Advertisement-
Play Games

所有知識體系文章,GitHub已收錄,歡迎Star!再次感謝,願你早日進入大廠! GitHub地址: https://github.com/Ziphtracks/JavaLearningmanual 01設計模式之單例設計模式 一、什麼是單例設計模式? 單例模式(Singleton Pattern) ...


所有知識體系文章,GitHub已收錄,歡迎Star!再次感謝,願你早日進入大廠!

GitHub地址: https://github.com/Ziphtracks/JavaLearningmanual

01設計模式之單例設計模式


一、什麼是單例設計模式?

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

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

註意:

  • 單例類只能有一個實例。
  • 單例類必須自己創建自己的唯一實例。
  • 單例類必須給所有其他對象提供這一實例。

二、單例設計模式的優缺點

優點:

  • 在記憶體中只有一個實例對象,減少記憶體開銷。解決了頻繁創建和銷毀記憶體實例對象的問題。
  • 避免過多的資源占用。比如:寫文件操作。

缺點:

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

三、單例設計模式的使用

當想要控制實例數目,節省系統資源的時候。並且在一個全局內,解決記憶體中頻繁創建和銷毀實例對象問題。

四、單例設計模式分類

單例設計模式的原則是創建唯一實例,但是創建唯一實例的方法有很多,也由此生成了許多種類的單例設計模式。它們分別是:普通懶漢式單例模式、同步鎖懶漢式單例模式、同步代碼塊懶漢式案例模式、餓漢式單例模式、雙重校驗鎖(雙檢鎖)單例模式、靜態內部類(登記式)單例模式和枚舉單例模式

五、單例模式思想的傳遞過程

問題: 如果我們要有寫單例設計模式的思想,該如何實現單例設計模式呢?怎樣才能實現全局內只創建一個實例化對象並使用呢?而且在使用過程中會不會出現其他問題呢?帶著疑問先把最基礎的單例設計模式寫出來!

首先,先創建一個類,類名為Singleton。然後去創建一個對象如下:

class Singleton {
    Singleton instance = new Singleton();
}

看到這裡,我們就發現這是一個普通的類,創建一個普通類的實例對象。那麼我們如果實現單個實例對象的話,就不能讓外界隨便來訪問創建該對象。所以我們就想到了構造方法,大家知道如果類中寫任何構造方法的話,它會隱式的存在一個公共的無參構造。這時候聰明的小伙伴想到了私有該構造器,不讓外界隨便創建對象。代碼如下:

class Singleton {
    //在類的內部創建一個類的實例對象
    private Singleton instance = new Singleton();
    //私有化構造器,使得在類的外部不能夠調用此構造器,隨意創建實例對象
    private Singleton() {}
}

那麼下一步呢?我們如何為外界提供該類內部的實例對象呢?有的小伙伴在創建類的實例對象的同時使用了修飾符static。我真的說著很聰明。這樣被static修飾了之後就可以通過類名來句點出來對象使用了?那麼問題來了。如果出現以下狀況怎麼辦呢?看以下操作!

class Singleton {
    //在類的內部創建一個類的實例對象,該靜態修飾的對象隨著類載入只創建一次實例
    private static Singleton instance = new Singleton();
    //私有化構造器,使得在類的外部不能夠調用此構造器,隨意創建實例對象
    private Singleton() {}
}

class Test {
    //分別創建了兩個對象為s1和s2,並且兩個對象是使用了同一個實例對象
    Singleton s1 = Singleton.instance;
    Singleton s2 = Singleton.instance;
    //不信的話,你可以比較一下兩個對象的地址
    System.out.println(s1 == s2);//結果true,證明是同一個對象    
}

結果很好,那麼我又來問問題了。如果創建了兩個對象,這時原來的實例對象改變了,會有什麼結果呢?那不就創建的兩個實例對象不是同一個了嘛。對,很對。不是同一個對象了。來再繼續看以下場景!

class Test {
    //又分別創建了兩個對象為s3和s4,這時我將原實例對象改變一下,把它置為空,會有什麼結果呢?
    Singleton s3 = Singleton.instance;
    //把原實例對象置為空
    Singleton.instance = null;
    Singleton s4 = Singleton.instance;
    //比較兩個對象的地址
    System.out.println(s1 == s2);//結果false,證明不是使用的同一個實例對象
}

因為上面的場景外界可以改變原來的實例對象,而造成創建實例不一致,那麼我們就想辦法限制外界更改實例。那肯定有小伙伴想到使用get方法,為類提供一個get方法,將創建好的實例對象提供給外界使用就ok了。那麼get方法是外界隨便就可以使用的嗎,常規來說,get方法是通過創建實例對象後句點出來的,那外界創建不了實例對象我們怎麼辦?別忘了我們有static修飾符,加了它不就能用類名句點出來嘛。代碼如下,此代碼也是最終版了!

//餓漢式單例模式
class Singleton {
    //1.在類的內部創建一個類的實例,該靜態修飾的對象隨著類載入只創建一次實例
    private static final Singleton instance = new Singleton();
    //2.私有化構造器,使得在類的外部不能夠調用此構造器
    private Singleton() {

    }
    //3.私有化此對象,通過公共的方法來調用
    //4.公共的方法,只能通過類來調用,因為設置為static的,同時類的實例也必須為static聲明的
    public static Singleton getInstance() {
        return instance;
    }
}

細心的小伙伴,會發現我在創建實例的時候不單單加了static修飾,而且還使用final修飾。這是為什麼呢?其實加final是為了該對象不被改變,是代碼更見健壯而已!

六、懶載入(Lazy Load)

懶載入(Lazy Load),這裡的懶載入指的是在使用實例對象的時候才會去創建實例對象。這就避免了資源的浪費和記憶體的占用問題。其實懶載入無非就是在空間換時間與時間換空間中的取捨!

再一次提問: 而且該實現還遺留了一個問題那就是,假如在此類中寫的代碼。我們不管用不用該實例對象,它類載入的時候就自動創建一個對象。會造成資源的浪費和記憶體的占用。雖然占用的不多,但是也是一種漏洞,懂吧!

那我我們考慮在單例模式思想傳遞過程中的終極版(此終極版就是餓漢式單例模式),它不支持懶載入。那怎樣才能支持懶載入呢?那我們就需要控制創建對象不在類載入的時候創建,而是在get方法中創建實例對象為外界提供。先看代碼吧,以下方法實現單例模式就支持懶載入了!

//普通懶漢式單例模式(線程不安全)
class Singleton {
    //創建實例對象
    private static Singleton instance = null;

    //私有化構造器
    private Singleton() {

    }

    //提供static修飾的get方法,以供外界創建實例對象
    public static Singleton getInstance() {
        //判斷實例對象是否為空,為空則創建實例對象並返回
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

七、單例設計模式的線程安全問題

問題: 在單例設計模式中什麼是線程不安全?

單例模式中,只會創建一個實例對象,也就是外界使用的實例對象是同一個對象,當然既然是同一個他們的地址都是相同的!所謂單例設計模式中的線程不安全,就是存在可以創建多個該實例對象的現象!

問題: 普通懶漢式單例模式是怎樣個線程不安全呢?如果將其改裝為線程安全的呢?

單例設計模式的線程安全問題,繼懶載入問題分析後,普通的懶漢式單例模式會存線上程安全問題。

在單例設計模式創建實例對象是一個原子操作!它的線程不安全,可以解釋為多個線程在併發訪問創建此單例對象時,同時在判空環節搶到了CUP的時間片,創建了兩個或多個該實例對象。破壞了單例設計模式單實例對象原則!

線程安全的單例模式有很多,比如介紹思想傳遞過程時的那個餓漢式單例模式,它天生就是線程安全的,你好好琢磨一下餓漢式,我不可能有創建多個實例的情況!

線程安全的單例模式,在第八章的分類剖析中,我會一一列舉,並將所有單例模式作對於寫出他們的特點、優缺點等等!

改裝普通懶漢式單例模式並解決線程安全問題

如果想要改裝普通懶漢式單例模式,我們就必須使用到同步鎖(synchronized)了!如下兩種操作可以解決普通懶漢式線程安全問題!代碼如下:

1.為原子操作方法加同步鎖

//同步鎖懶漢式單例模式(線程安全)
class Singleton {
    //創建實例對象
    private static Singleton instance = null;

    //私有化構造器
    private Singleton() {

    }

    //加同步鎖並被static修飾的get方法
    public synchronized static Singleton getInstance() {
        //具體來說以下是原子操作
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

2.為該實例對象加同步代碼塊

註意: 在使用同步代碼塊的時候,括弧內不能是this。因為我們使用static修飾創建對象,同步的對象不可能同步外界通過static句點出來的對象的,因為此操作並不合理。所以,此處寫了this會飄紅報錯!

//同步鎖懶漢式單例模式(線程安全)
class Singleton {
    //創建實例對象
    private static Singleton instance = null;

    //私有化構造器
    private Singleton() {

    }

    //加同步鎖並被static修飾的get方法
    public static Singleton getInstance() {
        //此處鎖的是實例對象
        synchronized(Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
}

八、單例模式分類剖析

5.1 餓漢式單例模式(推薦)

image-20200525223732985
image-20200525223732985

5.2 普通懶漢式單例模式

image-20200525230045774
image-20200525230045774

5.3 同步鎖懶漢式單例模式

image-20200525230504602
image-20200525230504602

5.4 同步代碼塊懶漢式單例模式

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 出現這種問題,我這裡遇到的原因是因為上一次查詢的結果集沒有釋放完全,導致查詢失敗 此時的錯誤內容是:Commands out of sync; you can't run this command now 該錯誤內容由mysql_error(conn)語句輸出。 如果你在上次查詢時忘了調用mysql ...
  • 方法引用 在使用Lambda表達式的時候,我們實際上傳遞進去的代碼就是一種解決方案:那參數做操作 弱國我們在Lambda中蘇哦指定的操作方案,已經有地方存在相同方案,那是否還有必要再寫重覆邏輯呢,答案肯定時沒有必要。 我們通過方法引用使用已經存在的方案 public interface PrintT ...
  • import pandasexcel=pandas.read_excel('現代徵信學.xlsx',index_col='列名',header=None) #header預設為0,指讀取第幾行,0代表第一行excel.columns=['Id','title','hh','uei','t'] #重新 ...
  • Java生鮮電商平臺-生鮮小程式首頁系統功能詳解(小程式/APP) 說明:對於一個成熟的生鮮小程式而言,首頁至關重要,下麵我就列舉,我們系統中的首頁的功能,本文只是簡單的介紹下,以達到拋磚引玉的作用,希望對大家有點幫助。 QQ:137071249 共同學習QQ群:793305035 ...
  • 軟體開發的生命周期 既然我們以後從事的工作時軟體開發,那麼我們就要對軟體開發的流程先做瞭解,畢竟要從娃娃抓起。早點瞭解軟體開發流程,也就有了軟體開發的思想與動力。 軟體開發的生命周期 問題定義 問題定義 是軟體定義時期的第一個階段。作為軟體的開發者,在這個階段必須弄清用戶“需要電腦解決什麼問題”。 ...
  • import pandasa=pandas.DataFrame({'ID':[1,2,3],'Name':['Time','Mike','Nick']})b=a.set_index('ID') #重新設置索引b.to_excel(r'D:\電腦二級\ocr 訓練\a.xlsx') ...
  • 面完螞蟻後,早就聽聞拼多多這個獨角獸,決定也去面一把。首先我在脈脈找了一個拼多多的HR,加了微信聊了下,發了簡歷便開始我的拼多多面試之旅。這裡要非常感謝拼多多HR小姐姐,從面試內推到offer確認一直都在幫我,人真的很nice。 ...
  • from matplotlib import pyplotimport numpyx=numpy.random.randn(100) #生成100個隨機數,關於標準正態分佈 y=-x+numpy.random.randn(100)*0.5pyplot.scatter(x,y,s=25,c='pink ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...