Java設計模式之單例模式

2021-08-20 12:23:58 字數 2398 閱讀 7607

所謂單例,指的就是單例項,有且僅有乙個類例項,這個單例不應該由人來控制,而應該由**來限制,強制單例。

單例有其獨有的使用場景,一般是對於那些業務邏輯上限定不能多例只能單例的情況,例如:類似於計數器之類的存在,一般都需要使用乙個例項來進行記錄,若多例計數則會不準確。

其實單例就是那些很明顯的使用場合,沒有之前學習的那些模式所使用的複雜場景,只要你需要使用單例,那你就使用單例,簡單易理解。所以我認為有關單例模式的重點不在於場景,而在於如何使用。

(1)懶漢式

何為懶?顧名思義,就是不做事,這裡也是同義,懶漢式就是不在系統載入時就建立類的單例,而是在第一次使用例項的時候再建立。

詳見下方**示例:

public class lhandanli 

//定義乙個公共的公開的方法來返回該類的例項,由於是懶漢式,需要在第一次使用時生成例項,所以為了執行緒安全,使用synchronized關鍵字來確保只會生成單例

public static synchronized lhandanli getinstance()

return dl;

}}

(2)餓漢式

又何為餓?餓者,飢不擇食;但凡有食,必急食之。此處同義:在載入類的時候就會建立類的單例,並儲存在類中。

詳見下方**示例:

public class ehandanli 

//定義公開方法,返回已建立的單例

public static ehandanli getinstance()

}

何為雙重加鎖機制?

在懶漢式實現單例模式的**中,有使用synchronized關鍵字來同步獲取例項,保證單例的唯一性,但是上面的**在每一次執行時都要進行同步和判斷,無疑會拖慢速度,使用雙重加鎖機制正好可以解決這個問題:

public class slhandanli 

public static slhandanli getinstance()}}

return dl;

}}

看了上面的**,有沒有感覺很無語,雙重加鎖難道不是需要兩個synchronized進行加鎖的嗎?

其實不然,這裡的雙重指的的雙重判斷,而加鎖單指那個synchronized,為什麼要進行雙重判斷,其實很簡單,第一重判斷,如果單例已經存在,那麼就不再需要進行同步操作,而是直接返回這個例項,如果沒有建立,才會進入同步塊,同步塊的目的與之前相同,目的是為了防止有兩個呼叫同時進行時,導致生成多個例項,有了同步塊,每次只能有乙個執行緒呼叫能訪問同步塊內容,當第乙個搶到鎖的呼叫獲取了例項之後,這個例項就會被建立,之後的所有呼叫都不會進入同步塊,直接在第一重判斷就返回了單例。至於第二個判斷,個人感覺有點查遺補漏的意味在內(期待高人高見)。

補充:關於鎖內部的第二重空判斷的作用,當多個執行緒一起到達鎖位置時,進行鎖競爭,其中乙個執行緒獲取鎖,如果是第一次進入則dl為null,會進行單例物件的建立,完成後釋放鎖,其他執行緒獲取鎖後就會被空判斷攔截,直接返回已建立的單例物件。

不論如何,使用了雙重加鎖機制後,程式的執行速度有了顯著提公升,不必每次都同步加鎖。

其實我最在意的是volatile的使用,volatile關鍵字的含義是:被其所修飾的變數的值不會被本地執行緒快取,所有對該變數的讀寫都是直接操作共享記憶體來實現,從而確保多個執行緒能正確的處理該變數。該關鍵字可能會遮蔽掉虛擬機器中的一些**優化,所以其執行效率可能不是很高,所以,一般情況下,並不建議使用雙重加鎖機制,酌情使用才是正理!

餓漢式會占用較多的空間,因為其在類載入時就會完成例項化,而懶漢式又存在執行速率慢的情況,雙重加鎖機制呢?又有執行效率差的毛病,有沒有一種完美的方式可以規避這些毛病呢?

貌似有的,就是使用類級內部類結合多執行緒預設同步鎖,同時實現延遲載入和執行緒安全。

public class classinnerclassdanli 

private classinnerclassdanli(){}

public static classinnerclassdanli getinstance()

}

如上**,所謂類級內部類,就是靜態內部類,這種內部類與其外部類之間並沒有從屬關係,載入外部類的時候,並不會同時載入其靜態內部類,只有在發生呼叫的時候才會進行載入,載入的時候就會建立單例例項並返回,有效實現了懶載入(延遲載入),至於同步問題,我們採用和餓漢式同樣的靜態初始化器的方式,借助jvm來實現執行緒安全。

其實使用靜態初始化器的方式會在類載入時建立類的例項,但是我們將例項的建立顯式放置在靜態內部類中,它會導致在外部類載入時不進行例項建立,這樣就能實現我們的雙重目的:延遲載入和執行緒安全。

在spring中建立的bean例項預設都是單例模式存在的。

java設計模式之單例模式

單位面試題目中有一道關於單例模式的題目.大多數參加筆試的同事都會寫出getinstance這個函式,但是當我問起什麼情況下需要使用單例 單例具體有什麼好處的時候 static函式也可以構建唯一例項,為什麼還需要單例.從使用單例的經驗來看,如下場景下,個人感覺應該使用單例 1.如果需要構建乙個工具類,...

java設計模式 之 單例模式

單例模式 有些類在程式執行過程中只需要儲存乙個例項,比如檔案管理類,音訊管理類,那麼我們如何實現乙個單例類呢?有以下幾點 1 定義乙個靜態變數 2 建構函式私有化 3 提供乙個public 靜態方法,獲取這個例項 4 一定要做執行緒同步 public static class singleton p...

Java設計模式之單例模式

說到單例模式 大家一定非常熟悉 這是最常見也是設計模式中相對簡單 好理解的設計模式 今天就跟大家掰一掰單例模式 單例模式確保某個類只有乙個例項 應用場景 執行緒池 快取 等物件常常被設計成單例 然而單例分兩種 1 惡漢式 2 懶漢式 我們先來看下 惡漢式 public class singleton...