1. 程式人生 > >單例模式深入理解(轉)

單例模式深入理解(轉)

一. 什麼是單例模式

因程式需要,有時我們只需要某個類同時保留一個物件,不希望有更多物件,此時,我們則應考慮單例模式的設計。

二. 單例模式的特點

1. 單例模式只能有一個例項。

2. 單例類必須建立自己的唯一例項。

3. 單例類必須向其他物件提供這一例項。

三. 單例模式VS靜態類

在知道了什麼是單例模式後,我想你一定會想到靜態類,“既然只使用一個物件,為何不乾脆使用靜態類?”,這裡我會將單例模式和靜態類進行一個比較。

1. 單例可以繼承和被繼承,方法可以被override,而靜態方法不可以。

2. 靜態方法中產生的物件會在執行後被釋放,進而被GC清理,不會一直存在於記憶體中。

3. 靜態類會在第一次執行時初始化,單例模式可以有其他的選擇,即可以延遲載入。

4. 基於2, 3條,由於單例物件往往存在於DAO層(例如sessionFactory),如果反覆的初始化和釋放,則會佔用很多資源,而使用單例模式將其常駐於記憶體可以更加節約資源。

5. 靜態方法有更高的訪問效率。

6. 單例模式很容易被測試。

幾個關於靜態類的誤解:

誤解一:靜態方法常駐記憶體而例項方法不是。

實際上,特殊編寫的例項方法可以常駐記憶體,而靜態方法需要不斷初始化和釋放。

誤解二:靜態方法在堆(heap)上,例項方法在棧(stack)上。

實際上,都是載入到特殊的不可寫的程式碼記憶體區域中。

靜態類和單例模式情景的選擇:

情景一:不需要維持任何狀態,僅僅用於全域性訪問,此時更適合使用靜態類。

情景二:需要維持一些特定的狀態,此時更適合使用單例模式。

四. 單例模式的實現

1. 懶漢模式

複製程式碼
public class SingletonDemo {
    private static SingletonDemo instance;
    private SingletonDemo(){

    }
    public static SingletonDemo getInstance(){
        if(instance==null){
            instance=new SingletonDemo();
        }
        return instance;
    }
}
複製程式碼

如上,通過提供一個靜態的物件instance,利用private許可權的構造方法和getInstance()方法來給予訪問者一個單例。

缺點是,沒有考慮到執行緒安全,可能存在多個訪問者同時訪問,並同時構造了多個物件的問題。之所以叫做懶漢模式,主要是因為此種方法可以非常明顯的lazy loading。

針對懶漢模式執行緒不安全的問題,我們自然想到了,在getInstance()方法前加鎖,於是就有了第二種實現。

2. 執行緒安全的懶漢模式

複製程式碼
public class SingletonDemo {
    private static SingletonDemo instance;
    private SingletonDemo(){

    }
    public static synchronized SingletonDemo getInstance(){
        if(instance==null){
            instance=new SingletonDemo();
        }
        return instance;
    }
}
複製程式碼

然而併發其實是一種特殊情況,大多時候這個鎖佔用的額外資源都浪費了,這種打補丁方式寫出來的結構效率很低。

3. 餓漢模式

複製程式碼
public class SingletonDemo {
    private static SingletonDemo instance=new SingletonDemo();
    private SingletonDemo(){

    }
    public static SingletonDemo getInstance(){
        return instance;
    }
}
複製程式碼

直接在執行這個類的時候進行一次loading,之後直接訪問。顯然,這種方法沒有起到lazy loading的效果,考慮到前面提到的和靜態類的對比,這種方法只比靜態類多了一個記憶體常駐而已。

4. 靜態類內部載入

複製程式碼
public class SingletonDemo {
    private static class SingletonHolder{
        private static SingletonDemo instance=new SingletonDemo();
    }
    private SingletonDemo(){
        System.out.println("Singleton has loaded");
    }
    public static SingletonDemo getInstance(){
        return SingletonHolder.instance;
    }
}
複製程式碼

使用內部類的好處是,靜態內部類不會在單例載入時就載入,而是在呼叫getInstance()方法時才進行載入,達到了類似懶漢模式的效果,而這種方法又是執行緒安全的。

5. 列舉方法

enum SingletonDemo{
    INSTANCE;
    public void otherMethods(){
        System.out.println("Something");
    }
}

Effective Java作者Josh Bloch 提倡的方式,在我看來簡直是來自神的寫法。解決了以下三個問題:

(1)自由序列化。

(2)保證只有一個例項。

(3)執行緒安全。

如果我們想呼叫它的方法時,僅需要以下操作:

public class Hello {
    public static void main(String[] args){
        SingletonDemo.INSTANCE.otherMethods();
    }
}

這種充滿美感的程式碼真的已經終結了其他一切實現方法了。

6. 雙重校驗鎖法

複製程式碼
public class SingletonDemo {
    private volatile static SingletonDemo instance;
    private SingletonDemo(){
        System.out.println("Singleton has loaded");
    }
    public static SingletonDemo getInstance(){
        if(instance==null){
            synchronized (SingletonDemo.class){
                if(instance==null){
                    instance=new SingletonDemo();
                }
            }
        }
        return instance;
    }
}
複製程式碼

接下來我解釋一下在併發時,雙重校驗鎖法會有怎樣的情景:

STEP 1. 執行緒A訪問getInstance()方法,因為單例還沒有例項化,所以進入了鎖定塊。

STEP 2. 執行緒B訪問getInstance()方法,因為單例還沒有例項化,得以訪問接下來程式碼塊,而接下來程式碼塊已經被執行緒1鎖定。

STEP 3. 執行緒A進入下一判斷,因為單例還沒有例項化,所以進行單例例項化,成功例項化後退出程式碼塊,解除鎖定。

STEP 4. 執行緒B進入接下來程式碼塊,鎖定執行緒,進入下一判斷,因為已經例項化,退出程式碼塊,解除鎖定。

STEP 5. 執行緒A初始化並獲取到了單例例項並返回,執行緒B獲取了線上程A中初始化的單例。

理論上雙重校驗鎖法是執行緒安全的,並且,這種方法實現了lazyloading。

相關推薦

模式深入理解

一. 什麼是單例模式因程式需要,有時我們只需要某個類同時保留一個物件,不希望有更多物件,此時,我們則應考慮單例模式的設計。二. 單例模式的特點1. 單例模式只能有一個例項。2. 單例類必須建立自己的唯一例項。3. 單例類必須向其他物件提供這一例項。三. 單例模式VS靜態類在知道了什麼是單例模式後,我想你一定會

模式面試題特點、理解

幫同事出面試題,為難之際、又逢人指點,現整理如下 (1)單例模式特點(什麼是單例模式)?  a.單例類只能有一個例項。  b.單例類必須自己建立自己的唯一例項。  c.單例類必須給所有其他物件提供這一例項。 (2)單例模式的作用(用單例模式的目的)? Singleton

模式的實現餓漢式和懶漢式

null cte get pri single singleton ins 安全 tin 1.懶漢模式。 class Singleton { private: static Singleton* m_instance; Singleton(

模式新談包含三種方式

  設計模式是一套被反覆使用,多數人知曉,經過分類編目的,程式碼設計的總結,也可以說是前人的智慧結晶。學習設計模式能讓我們對一些應用場景使用相同的套路達到很好的效果,我會不定時更新一些自己對設計模式的理解的文章,從定義,實現,應用場景來說說設計模式,今天我要說的物件是單例模式一,定義  什麼是單例模式,字面理

模式程式碼實現內作業

單例模式理論知識: 單例模式官方定義:確保一個類只有一個例項,並提供全域性訪問點來訪問這個唯一的例項。(全域性訪問點:實際就是全域性變數,主要解決物件隨時都可以被訪問,但是不能防止建立多個物件這個問題) 單例模式的實現:               1、單例模式的目的:

模式--餓漢按需建立、懶漢多執行緒以及多執行緒下改進

程式碼註釋有介紹,(排版過於難受,下次用md排完再傳上來) package singleton; /** * @author: ycz * @date: 2018/12/24 0024 22:15 * @description: 懶漢模式 */ public class SingleTon1

模式詳解餓漢式,懶漢式,登記式,列舉式,序列化和反序列

一、單例模式應用場景:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 應用場景:保證一個類僅有一個例項,並提供一個訪問它的全域性訪問點。 Spring 中的單例模式完成了後半句話,即提供了全域性的訪問點 BeanFactory。但沒有從構造器級別去 控制單例,這是

對代理模式理解

代理,代表打理,以他人的名義代表委託人打理其本職工作之外或不所能及的事務,達成合作關係並更高效地促成事務完成的目的。例如明星經紀人,他們並沒有像明星一樣會唱歌、跳舞或演戲,而是替明星打理一些無暇顧及的事務(這並不代表可以代理分外之事),比如推廣與宣傳,合同談判啊之類,達成和約後他們才會通知明星去表演

劍指offer:面試題二:模式的實現使用C++語言

#include <iostream> #include <iomanip> #include <cstdio> #include <cstdlib>

JAVA設計模式模式-快速理解Java中的五種模式()

百度百科對單例模式的介紹 顯然單例模式的要點有三個;一是某個類只能有一個例項;二是它必須自行建立這個例項;三是它必須自行向整個系統提供這個例項。 從具體實現角度來說,就是以下三點:一是單例模式的類只提供私有的建構函式,二是類定義中含有一個該類的靜態私有物件,三是該

【Android基礎】Activity深入理解——Activity棧和載入模式

之前的一篇文章中詳細說明了Activity的生命週期,說明了Activity中的回撥方法是如何被觸發的。在使用者使用App時,每一個 Activity 都處於某一個狀態,對於開發者來說,是無法控制其應用程式處於某一個狀態的,這些均由系統來完成。那麼Activit

Vue2.0 探索之路——生命周期和鉤子函數的一些理解

head chrom 路由 技術分享 defined 修改 疑問 reat 有時 前言 在使用vue一個多禮拜後,感覺現在還停留在初級階段,雖然知道怎麽和後端做數據交互,但是對於mounted這個掛載還不是很清楚的。放大之,對vue的生命周期不甚了解。只知道簡單的使用,而不

JavaScript深入理解

有一點 相同 定義 怎麽辦 turn 如何 nbsp 屬性。 fff 強大的原型和原型鏈 前言 JavaScript 不包含傳統的類繼承模型,而是使用 prototypal 原型模型。 雖然這經常被當作是 JavaScript 的缺點被提及,其實基於原型的繼承模型比傳

Java模式深入詳解

protected test 異常 except while 深入 bject his 不一致 Java單例模式深入詳解 一.問題引入   偶然想想到的如果把Java的構造方法弄成private,那裏面的成員屬性是不是只有通過static來訪問呢;如果構造方法是privat

矩陣乘法的理解

導致 ebr tle 就會 ice 大學 img 很多 所有 大多數人在高中,或者大學低年級,都上過一門課《線性代數》。這門課其實是教矩陣。 剛學的時候,還蠻簡單的,矩陣加法就是相同位置的數字加一下。 矩陣減法也類似。 矩陣乘以一個常數,就是所有位置都乘以這個數。

Spartan6芯片配置模式詳解

roc gen bit png 寬度 con 讀取配置 3.2 多個 1. 配置概述  Spartan6系列FPGA通過把應用程序數據導入芯片內部存儲器完成芯片的配置。Spart-6 FPGA可以自己從外部非易失性存儲器導入編程數據,或者通過外界的微處理器、DSP等對其進行

OpenCV中對Mat裏面depth,dims,channels,step,data,elemSize和數據地址計算的理解

ima 忽略 learning note 數組 進行 每一個 ber 初始 cv::Matdepth/dims/channels/step/data/elemSizeThe class Mat represents an n-dimensional dense numeri

設計模式六大原則

method 過多 這樣的 不同 提高 依賴倒置 同心圓 23種設計模式 變化 設計模式六大原則(1):單一職責原則 定義:不要存在多於一個導致類變更的原因。通俗的說,即一個類只負責一項職責。 問題由來:類T負責兩個不同的職責:職責P1,職責P2。當由於職責P1需求發

Spring中AOP的一個通俗易懂的理解

學會 事物 nbsp 連接 新的 之前 天都 這不 proxy 這是看到的一個最易懂得AOP簡介了,適合初學者理解。 轉自:http://www.verydemo.com/demo_c143_i20837.html 1.我所知道的aop   初看aop,上來就是一大堆術語,

Java中泛型T和Class<T>以及Class<?>的理解

tcl ota 特定 類型 基本 ext pla enum extend 註意:class是java的關鍵字, 在聲明Java類時使用; Class類的實例表示Java應用運行時的類(class ans enum)或接口(interface and annotatio