設計模式之單例模式

2021-09-13 23:25:14 字數 3014 閱讀 1549

所謂單例模式:保證乙個類僅有乙個例項 並提供乙個訪問它的全域性訪問點。

簡單的說:單例設計模式就是乙個類有且僅有乙個物件。

通常我們可以讓乙個全域性變數使得乙個物件被訪問,但它不能防止你例項化多個物件。乙個最好的方法就是,讓類自身負責儲存它的唯一例項。這個類可以保證沒有其他例項被建立,並且它可以提供乙個訪問該例項的方法。

單例模式分為:1.餓漢式 2.懶漢式

餓漢式:在類載入的時候,直接將唯一的物件建立出來。

懶漢式:在類載入的時候,並不直接建立這個唯一的物件。而是在呼叫方法時,去判斷是否建立過物件。這裡分為兩種情況:1.如果是已經建立過物件,那麼直接返回。2.如果並沒有建立乙個物件,則建立乙個物件 將其位址值返回。

餓漢式:

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

2.在類的內部產生乙個唯一的例項化物件,並且將其封裝成private static型別。

3.通過乙個靜態方法返回這個唯一物件

餓漢式**如下:
public class singleton 

// 靜態方法返回該例項

public static singleton getinstance()

}

餓漢式的優點:實現起來簡單,沒有多執行緒同步問題。

餓漢式的缺點:提前占用系統資源。(不管你用不用這個例項化物件,隨著類的載入他已經被建立出來了。因此會占用記憶體)當類被解除安裝時,靜態變數被摧毀,並釋放所占用的記憶體,因而在某些特定情況下會耗費記憶體。

懶漢式:

懶漢式是延遲載入,在呼叫get()方法時,例項才被建立。(先不著急例項化物件,等要用的時候才給你建立出來。不著急,所以稱為"懶漢式"),常見的實現懶漢式方法就是在get()方法中new乙個例項化物件。

懶漢式**如下:

public class singleton 

// 靜態方法返回該例項

public static singleton getinstance()

return instance;

}}

在這裡 我們會先進行乙個判斷 如果物件不為null,那麼直接返回。如果物件為null,那麼使用new關鍵字建立出這個物件。

懶漢式優點:在第一次呼叫時才建立物件,節約記憶體。

懶漢式缺點:在多執行緒的環境下,這種實現方法根本無法保證單例的狀態。

執行緒安全的懶漢式**如下:

public class singleton

// 靜態方法返回該例項,加synchronized關鍵字實現同步

public static synchronized singleton getinstance()

return instance;

}}優點:在多執行緒的環境下,保證了」懶漢式」的執行緒安全問題。

缺點:總所周知在多執行緒環境下,synchronized方法通常效率低。因此這並不是最佳的實現方案。

dcl雙檢查鎖機制(dcl:double checked locking)

public class singleton 

// 靜態方法返回該例項

public static singleton getinstance() }}

return instance;

}}

關於為什麼要做兩次判斷的解釋: 對於instance存在的情況,就直接返回,這裡沒有問題。但是當instance為null並且有兩個執行緒同時呼叫getinstance()方法時,這裡它們都能通過第一次 instance == null 判斷。然後由於同步**塊 synchronized,這兩個執行緒只能有乙個進入,另乙個在外排隊等待,必須要等第乙個進去並出來以後,第二個才能進入。這裡如果沒有了第二次 instance==null的判斷,則第乙個執行緒進入建立了例項,在它出來之後,第二個執行緒依然可以進入建立例項。這樣就沒有達到單例的目的。

經過分析,貌似上面縮寫的dcl**是沒有問題了。但是,如果我們深入分析,那麼還是可能存在一定的問題。

如果我們深入到jvm的層面去**dcl這段**,那麼就有可能(僅僅是有可能)會產生問題。

這裡從jvm建立物件所經歷的步驟開始分析。因為虛擬機器在執行建立例項的這一步操作的時候,其實是分了好幾步去進行的,也就是說建立乙個新的物件並非是原子性操作。在有些jvm中上述做法是沒有問題的,但是有些情況下是會造成莫名的錯誤。

jvm建立物件需要經過三步:

1.分配記憶體

2.初始化構造器

3.將物件指向分配的記憶體的位址

這種順序針對上述dcl**中是完全可以執行的,因為jvm執行了整個流程建立了物件才將記憶體位址交給了物件。但是,如果2.3調換了順序那麼會有問題嗎?答案是肯定的!(2.3可能調換順序的原因是jvm會針對位元組碼調優,其中有一項就是調整指令的執行順序)

如果2.3調換了順序,那麼記憶體位址首先就會交給物件。針對上面的dcl**,記憶體位址會先交給instance 物件,然後再進行初始化構造器。這時,後面的執行緒去請求getinstance()方法時,會認為instance物件已經是被建立了,然後就會返回乙個引用。但是如果在初始化構造器之前,這個執行緒去使用了instance物件,就會產生莫名的錯誤。

靜態內部類**如下:

public class singleton 

private singleton()

private static final singleton getinstance()

}

這種靜態內部類實現單例模式是可以完全避免上述錯誤的!乙個類的靜態屬性只會在第一次載入類時初始化。同時,jvm保證在類載入的過程中static**塊在多執行緒或者單執行緒下都正確執行,且僅執行一次。在初始化進行一半的時候,別的執行緒是無法使用的,因為jvm會幫我們強行同步這個過程。另外由於靜態變數只初始化一次,所以singleton仍然是單例的。解決了延遲載入以及執行緒安全的問題。

最後 學友哥鎮樓:

設計模式之單例模式

前一段時間買了一本秦小波寫的 設計模式之禪 網上對這書的評價很高。現在還沒有看很多,但是有些地方頗有感觸,也並不是所有的地方都能看懂,但是會慢慢研究的。自己對於設計模式的感覺就是乙個字 牛!感覺會23種設計模式並且會熟練運用的人,真的就是大師級的牛人了,設計模式是乙個專案主管或者架構師一定要會的東西...

設計模式之單例模式

package com.xie.singleton public class singleton 提供乙個共有的靜態的入口方法 public static singleton getinstance 懶漢式 延遲載入 提供乙個私有的靜態的成員變數,但不做初始化 private static sing...

設計模式之 單例模式

單例模式 singleton 保證乙個類僅有乙個例項,並提供乙個訪問它的全域性訪問點。單例模式 單件模式 使用方法返回唯一的例項 public class singleton private static singleton instance public static singleton geti...