關於單例模式的理解(二)

2021-10-23 09:18:35 字數 2965 閱讀 6051

/**

* 懶漢模式

* 單例例項在第一次使用時進行建立

*/@threadsafe

@notrecommend

public

class

singletonexample3

// 單例物件

private

static singletonexample3 instance = null;

// 靜態的工廠方法

public

static

synchronized singletonexample3 getinstance()

return instance;

}}

這樣就是執行緒安全了,但是使用synchronized這樣的阻塞同步,即同一時刻只有乙個執行緒來訪問的方式是會有比較大的效能開銷的,這是我們所不希望看到的,所以不太推薦。

於是,有聰明的小夥伴一定會想到,你之前的那個是synchronized修飾方法的,我只要將其放進方法內只需要同步必要的**不就好了,如下圖所示

/**

* 懶漢模式 -> 雙重同步鎖單例模式

*/@notthreadsafe

public

class

singletonexample4

// 單例物件

private

static singletonexample4 instance = null;

// 靜態的工廠方法

public

static singletonexample4 getinstance()

}}return instance;

}}

這時候,很多小夥伴就覺得這樣很ok啊,的確啊,看上去比較華麗,但我想提醒的這樣的寫法是執行緒不安全的。肯定有小夥伴會覺得懵逼,這怎麼就執行緒不安全了,且聽我為你娓娓道來。

首先我們需要了解的是,cpu的指令,當我們在instance = new singletonexample4();執行的時候,我們將其放大細化,主要是分成三步:

// 1、memory = allocate() 分配物件的記憶體空間

// 2、ctorinstance() 初始化物件

// 3、instance = memory 設定instance指向剛分配的記憶體

上述三步驟,在經過jvm的指令優化後,其第二點和第三點會步驟互換

// jvm和cpu優化,發生了指令重排

// 1、memory = allocate() 分配物件的記憶體空間

// 3、instance = memory 設定instance指向剛分配的記憶體

// 2、ctorinstance() 初始化物件

所以我們現在假設有a,b兩個執行緒,當a執行緒走到132中的三,這時候b到if(instance == null)那邊就會發現,原來instance已經有值了,就直接返回了,然而啥都還沒發生呢,你就完事了。因為還沒有例項呢。。。,這時候肯定會出現問題。

@notthreadsafe

public

class

singletonexample4

// 1、memory = allocate() 分配物件的記憶體空間

// 2、ctorinstance() 初始化物件

// 3、instance = memory 設定instance指向剛分配的記憶體

// jvm和cpu優化,發生了指令重排

// 1、memory = allocate() 分配物件的記憶體空間

// 3、instance = memory 設定instance指向剛分配的記憶體

// 2、ctorinstance() 初始化物件

// 單例物件

private

static singletonexample4 instance = null;

// 靜態的工廠方法

public

static singletonexample4 getinstance()

}}return instance;

}}

雖然這個機率不大,但嚴格的說這並不是執行緒安全的。那麼什麼關鍵字能防止指令的重排序呢?熟悉的同學,一定會想到乙個關鍵字,沒錯,就是volatile, 就是那個能有可見性,防指令重排卻沒有原子性的volatile。如下如**所示:

@threadsafe

public

class

singletonexample5

// 1、memory = allocate() 分配物件的記憶體空間

// 2、ctorinstance() 初始化物件

// 3、instance = memory 設定instance指向剛分配的記憶體

// 單例物件 volatile + 雙重檢測機制 -> 禁止指令重排

private

volatile

static singletonexample5 instance = null;

// 靜態的工廠方法

public

static singletonexample5 getinstance()

}}return instance;

}}

/**

* 列舉模式:最安全

*/@threadsafe

@recommend

public

class

singletonexample7

public

static singletonexample7 getinstance()

private

enum singleton

public singletonexample7 getinstance()

}}

這是一種基於列舉的實現單例的方式。

關於單例模式

單例模式 只能產生乙個例項物件 思路 不能外部new,只能內部new 構造方法要私有,構造方法私有後只能類自己呼叫 需要將物件 通過類自己的來構造例項 的引用變數設定成static修飾的這樣就屬於類本身,只有乙份,即乙個例項物件 構造的物件需要提供給外部,由於無法new物件來呼叫這個方法,因此需要乙...

關於單例設計模式的淺顯理解

首先,讓我們先來看看下面的乙個單例模式 public class singleton public synchronized static singleton getinstance 由於本類的構造方法是私有的,因此其他類無法通過new singleton 來建立本類的乙個例項,但本類提供了乙個公有...

關於單例模式的心結

今天重構公司的 看到有乙個單例的物件,在整個系統中到處都存在呼叫它的身影,因為我們這個專案會在應用伺服器 server 第一次啟動的時候載入資料庫中的 表 為什麼要先載入,因為這個專案採用的是c s模式,利用remoting進行通訊,在客戶端啟動的時候會多次獲取 表中的不同資料。複製 1 publi...