看這裡!一文帶你讀懂單例模式

2021-03-02 Java隨筆錄

點擊上方 [Java隨筆錄]
關注可了解更多Java相關的技術分享。問題或建議,歡迎公眾號留言!

單例模式

單例模式可以說是設計模式中最簡單的一個了,它屬於創建型模式,其定義如下:

單例模式確保某個類只有一個實例,而且自行實例化並向整個系統提供這個實例。

在應用這個模式時,單例對象的類必須保證只有一個實例存在。許多時候整個系統只需要擁有一個的全局對象,這樣有利於我們協調系統整體的行為。比如在某個伺服器程序中,該伺服器的配置信息存放在一個文件中,這些配置數據由一個單例對象統一讀取,然後服務進程中的其他對象再通過這個單例對象獲取這些配置信息。這種方式簡化了在複雜環境下的配置管理。

實現單例模式的思路是:一個類能返回對象一個引用(永遠是同一個)和一個獲得該實例的方法(必須是靜態方法,通常使用getInstance這個名稱);當我們調用這個方法時,如果類持有的引用不為空就返回這個引用,如果類保持的引用為空就創建該類的實例並將實例的引用賦予該類保持的引用;同時我們還將該類的構造函數定義為私有方法,這樣其他處的代碼就無法通過調用該類的構造函數來實例化該類的對象,只有通過該類提供的靜態方法來得到該類的唯一實例。單例模式的類圖如下所示:

單例模式的應用場景

對於Java來說,單例模式可以保證在一個 JVM 中只存在單一實例。單例模式的應用場景主要有以下幾個方面;

需要頻繁創建的一些類,使用單例可以降低系統的內存壓力,減少GC;

某類只要求生成一個對象的時候,如一個班中的班長、每個人的身份證號等;

某些類創建實例時佔用資源較多,或實例化耗時較長,且經常使用;

某類需要頻繁實例化,而創建的對象又頻繁被銷毀的時候,如多線程的線程池、網絡連接池等;

頻繁訪問資料庫或文件的對象;

對於一些控制硬體級別的操作,或者從系統上來講應當是單一控制邏輯的操作,如果有多個實例,則系統會完全亂套;

當對象需要被共享的場合。由於單例模式只允許創建一個對象,共享該對象可以節省內存,並加快對象訪問速度。如Web中的配置對象、資料庫的連接池等。

單例模式的優缺點

單例模式的優點:

單例模式的缺點:

單例模式一般沒有接口,擴展困難。如果要擴展,則除了修改原來的代碼,沒有第二種途徑,違背開閉原則;

在並發測試中,單例模式不利於代碼調試。在調試過程中,如果單例中的代碼沒有執行完,也不能模擬生成一個新的對象;

與單一職責原則衝突,一個類只應該關心其內部邏輯的實現,而不應該插手外部對其的實例化。

代碼實現

單例模式的代碼實現其實很簡單,要做到一個類的實例只能由類自身進行創建,關鍵就是私有化其構造函數。其中,單例模式在多線程的應用場合下必須小心使用。如果當唯一實例尚未創建時,有兩個線程同時調用創建方法,那麼它們同時沒有檢測到唯一實例的存在,從而同時各自創建了一個實例,這樣就有兩個實例被構造出來,從而違反了單例模式中實例唯一的原則。結合不同的場景需求(單線程多線程)以及Java的特性(鎖和類加載),就會有很多種實現方法,我這裡總結了常見的五種。

(1) 餓漢式

餓漢式實現起來簡單,它基於類加載機制,不用加鎖也能避免多線程同步的問題,所以執行效率會比較高。但是這種方法有個缺點,就是在類加載的時候就會進行實例化,沒有達到Lazy Loading的效果。該種方法的代碼實現如下:

1public class Singleton {
2
3    private Singleton() {
4
5    }
6
7    private static final Singleton instance = new Singleton();
8
9    public static Singleton getInstance() {
10        return instance;
11    }
12
13}

(2) 懶漢式(線程不安全)

針對餓漢式中沒有Lazy Loading的效果,可以通過如下方法來實現。不過該方法也有個最大的問題,就是只能支持在單線程環境下工作,它是線程不安全的,多線程的話會出現多個實例化對象的出現。具體代碼如下:

1public class Singleton {
2
3    private Singleton() {
4
5    }
6
7    private static Singleton instance;
8
9    public static Singleton getInstance() {
10        if (instance == null) {
11            instance = new Singleton();
12        }
13        return instance;
14    }
15
16}

(3) 懶漢式(線程安全)

針對線程不安全的問題,最簡單直接的解決方法就是加鎖,對getInstance( )方法加鎖,這能很快的解決多線程安全問題,但是帶來的問題也很明顯,就是太影響性能了。具體代碼如下所示:

1public class Singleton {
2
3    private Singleton() {
4
5    }
6
7    private static Singleton instance;
8
9    synchronized public static Singleton getInstance() {
10        if (instance == null) {
11            instance = new Singleton();
12        }
13        return instance;
14    }
15
16}

(4) 雙重校驗鎖(double-checked locking)

直接對整個getInstance( )方法上鎖,能夠解決線程安全問題,但會大大影響程序的性能,因為很容易出現鎖競爭的情況。這裡使用雙重校驗的機制,一方面能夠解決線程安全問題,另一方面還能保持高性能。關鍵就是在於兩個if判斷,其實大部分情況下外部的if判斷是不會通過的,因為只要該單例類被實例化了一次,外部if的判斷條件就成立不了了,所以能夠保持較好的性能;而內部的if判斷,則針對的是單例類還沒被實例化的時候,可能有多個線程同時通過了外部的if判斷,要對單例類進行實例化,此時就需要上鎖,並在臨界區內再次判斷實例是否已經被創建,防止多次創建,因此內部的if判斷保證了只有一個實例會被創建。具體代碼如下:

1public class Singleton {
2
3    private Singleton() {
4
5    }
6
7    private static volatile Singleton instance;
8
9    public static Singleton getInstance() {
10        if (instance == null) {
11            synchronized (Singleton.class) {
12                if (instance == null) {
13                    instance = new Singleton();
14                }
15            }
16        }
17        return instance;
18    }
19
20}

(5) 靜態內部類

該方法能夠達到同雙重校驗方式相同的效果,且實現方法更為簡單,不需要藉助鎖就能實現。主要是藉助了類加載的特性,內部靜態類只有在使用的時候才會進行加載,這就能夠實現Lazy Loading;且類加載時只會加載一次,天然的線程安全保證。具體代碼如下:

1public class Singleton {
2
3    private Singleton() {
4
5    }
6
7    private static class InnerClass {
8        private static final Singleton instance = new Singleton();
9    }
10
11    public static Singleton getInstance() {
12        return InnerClass.instance;
13    }
14
15}

讚賞

相關焦點

  • 設計模式:單例模式
    基本概念1.1 原理單例模式可以說是所有設計模式中最簡單的一個了,這裡我們先直接給出它的概念然後再對它進行詳細的講解。單例模式就是:一個類只能有一個實例,並提供對該實例的全局訪問點。通俗地說,就是一個類只能創建一個對象,並且在程序的任何地方都能夠訪問到該對象。在某些情況下一些類只需要一個實例就夠了,我們以一個簡化的文件管理器作為例子來說明。
  • 單例模式襲來
    吹一波單例模式❝確保某個類只有一個實例,而且自行實例化並向整個系統提供整個實例。說白了,就是"獨苗"!什麼是"自行實例化"?就是整個實例是在類內部自己創建的,不是在別的類中創建的。❞單例誰都會寫,而且五花八門,且隨洒家來瞅瞅。
  • 深入淺出js單例模式
    何為單例模式?顧名思義,單例模式就是保證一個類僅有一個實例,也就是創建出來的兩個實例必須相等!
  • 設計模式一:單例模式
    什麼是單例模式單例模式是指系統中的某個類只能有一個對象實例。
  • 設計模式大冒險第四關:單例模式,如何成為你的「唯一」
    這一篇文章是關於設計模式大冒險系列的第四篇文章,這一系列的每一篇文章我都希望能夠通過通俗易懂的語言描述或者日常生活中的小例子來幫助大家理解好每一種設計模式。今天這篇文章來跟大家一起學習一下單例模式。相信讀完這篇文章之後,你肯定會有所收穫的。關於單例模式,這應該是設計模式中最簡單的一種了。
  • Java的單例模式
    一、什麼是單例模式?
  • 大話設計模式之愛你一萬年:第二章 創建型模式:單例模式::我的女朋友只有你一個:3.4.5.單例模式的實現-餓漢/靜態/枚舉
    >大話設計模式之愛你一萬年:第一章 設計模式基本概念:2.GoF的23種設計模式的分類和功能大話設計模式之愛你一萬年:第一章 設計模式基本概念:3.設計模式的六大原則大話設計模式之愛你一萬年:第二章 創建型模式:單例模式::我的女朋友只有你一個:1.單例模式的基本概念大話設計模式之愛你一萬年:第二章 創建型模式:單例模式::我的女朋友只有你一個:2.
  • 深入解析單例模式的七種實現
    來源:http://t.cn/E4J3ffX什麼是單例模式如何實現單例模式呢?單例模式的七種實現總結什麼是單例模式什麼是單例模式呢?Show me the code讓我們來看看單例模式的7種實現方式單例模式的七種實現第一種:懶漢式加載懶漢式加載:最簡單的單例模式:2步,1.把自己的構造方法設置為私有的,不讓別人訪問你的實例,2.提供一個static方法給別人獲取你的實例.
  • Android設計模式(1)——單例模式
    單例模式使用的場景確保某個類有且只有一個對象的場景,避免產生多個對象消耗過多的資源,或者某種類型對象只應該有且只有一個,例如,創建一個對象需要消耗的資源過多,,如要訪問IO和資料庫等資源,這時就需要考慮使用單例模式。
  • Java單例模式深入詳解
    構造函數弄成private 就是單例模式,即不想讓別人用new 方法來創建多個對象,可以在類裡面先生成一個對象,然後寫一個public static方法把這個對象return出去。(eg:public 類名 getInstancd(){return 你剛剛生成的那個類對象;}),用static是因為你的構造函數是私有的,不能產生對象,所以只能用類名調用,所有只能是靜態函數。
  • Python設計模式之單例模式
    什麼場景會用到單例模式呢?就是在系統工程中只想創建單個實例對象,比如資料庫連接池,Redis連接池,服務監控中心等。這個場景下,如果存在多個實例對象,那麼會有無法預測的異常。同時,在設計單例模式時,需要考慮的很重要的因素就是並發性,即多線程調用時是否會存在多個線程。那麼,如何設計使用單例模式呢?
  • 手寫單例模式
    手寫單例模式為什麼要有單例模式: 在編程中,有些場景,是這樣的:
  • 單例模式絕對沒有想像的那麼簡單!不服來看!
    一、前言單例模式(Singleton Pattern)是 Java 中最常用的設計模式之一,同時也是面試的重災區。有些人可能覺的單例模式很簡單,沒有什麼難的。其實不然,因為牽扯到線程安全的問題,所以單例模式絕對能體現出你的功底。不信接著往下看。
  • PHP 單例模式的解析和實戰
    什麼是單例模式?1、含義   作為對象的創建模式,單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統全局地提供這個實例。它不會創建實例副本,而是會向單例類內部存儲的實例返回一個引用。2、單例模式的三個要點:(1).
  • 為什麼要用單例模式?
    我們在編程中最常用的模式就是單例模式了,然而單例模式都用在什麼場合?為什麼不用靜態方法而要用單例模式呢?
  • 【設計模式】各個擊破單例模式的8種寫法
    單例模式在一個系統開發過程中,我們在基於節省內存資源、保證數據內容的一致性的考慮上,往往需要對某些類要求只能創建一個實例,即「保證類只有一個實例」的設計模式就是單例模式。比如我們遇到過的各種Manager管理類,各種Factory工廠類;Spring 框架應用中的 ApplicationContext、資料庫中的連接池等也都是單例模式。本文旨在淺析一下單例模式的寫法。單例模式的8種寫法1.
  • Java 實現單例模式的 9 種方法
    來源:http://t.cn/EbMp32y一. 什麼是單例模式二. 單例模式的特點三. 單例模式VS靜態類四. 單例模式的實現一. 什麼是單例模式因進程需要,有時我們只需要某個類同時保留一個對象,不希望有更多對象,此時,我們則應考慮單例模式的設計。二.
  • java設計模式中的單例模式,收藏起來慢慢看!
    在java中,單例模式算是比較基礎和簡單的,今天就來簡單聊聊什麼是單例模式。比如說,一個應用程式中,某個類的實例對象只有一個,而我們沒有辦法new,因為構造器已經被private聲明了,通過getInstance()的方法可以獲取它們的實例。
  • 再見面試官:單例模式有幾種寫法?
    「「你知道茴香豆的『茴』字有幾種寫法嗎?」糾結單例模式有幾種寫法有用嗎?有點用,面試中經常選擇其中一種或幾種寫法作為話頭,考查設計模式和coding style的同時,還很容易擴展到其他問題。這裡講解幾種筆者常用的寫法,但切忌生搬硬套,去記「茴香豆的寫法」。
  • 什麼是python單例模式,看完你就知道了
    1、什麼是單例模式讓所有類在實例化時,指向同一個內存地址,稱之為單例模式 PS:無論產生多少個對象,都會指向 單個 實例當在確定 "類中的屬性與方法不變" 需要反覆調用類時,會產生不同的對象,也會產生不同的內存地址,最終造成資源的浪費