併發程式設計(七) 安全發布物件

2022-08-10 21:00:26 字數 4521 閱讀 5046

發布物件是指使乙個物件能夠被當前範圍之外的**所使用

物件逸出是一種錯誤的發布,指當乙個物件還沒有構造完成時,就使它被其他執行緒所見

@slf4j

public

class

escape

private

class

innerclass", escape.this

.thiscanbeescape);}}

public

static

void

main(string args)

}

在此例項中escape物件還沒有構造完成,就訪問了該物件的成員變數thiscanbeescape,該類是執行緒不安全的,並且非常不推薦這麼寫。

@slf4j

public

class

unsafepublish ;

public

string getstates()

public

static

void

main(string args) ", arrays.tostring(unsafepublish.getstates()));

unsafepublish.getstates()[0] = "d";

log.info("{}", arrays.tostring(unsafepublish.getstates()));

}}

輸出為:[a,b,c]和[d,b,c] 這樣發布的物件為執行緒不安全的,因為無法保證其他執行緒是否會修改states域,從而造成狀態錯誤

a、在靜態初始化函式中初始化乙個物件引用

b、將物件的引用儲存到volatile型別域或atomicreference物件中

c、將物件的引用儲存到某個正確構造物件的final型別域中

d、將物件的引用儲存到乙個由鎖保護的域中

我們以對不同單例的實現,來說明一下安全發布物件的方法

public

class

singletonexample1

//單例物件

private

static singletonexample1 instance = null

;

//靜態的工廠方法

public

static

singletonexample1 getinstance()

return

instance;

}}

此例項在單執行緒模式下沒有任何問題,但在多執行緒模式下,如兩個執行緒都同時執行到判斷instance==null時,就有可能new出兩個例項來,所以說這是執行緒不安全的,這也就是懶漢模式,此例項滿足了a條件,如果再加上d條件,在判斷是否為null時加鎖,就可以變為執行緒安全的

public

class

singletonexample2

//單例物件

private

static singletonexample2 instance = new

singletonexample2();

//靜態的工廠方法

public

static

singletonexample2 getinstance()

}

此demo是執行緒安全的,使用餓漢模式時需要注意兩點,一是此類肯定被使用(避免造成資源浪費),二是私有的構造方法中沒有過多的處理4

public

class

singletonexample3

//單例物件

private

static singletonexample3 instance = null

;

//靜態的工廠方法

public

static

synchronized

singletonexample3 getinstance()

return

instance;

}}

此demo即為demo1加鎖的情況,是執行緒安全的,但是並不推薦這麼寫,因為這樣雖然保證了執行緒安全,但在效能上有一定的開銷

/**

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

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

* 雙重檢測機制不一定執行緒安全,因為有指令重排的存在 */

public

class

singletonexample4

//單例物件

private

static singletonexample4 instance = null

;

//靜態的工廠方法

public

static

singletonexample4 getinstance() }}

return

instance;

}}

此demo是懶漢模式的優化版本,但注意此demo不是執行緒安全的,因為有指令重排的存在,當執行instance=new singletonexample4()時,cpu會執行三步操作:1、memory=allocate() 分配物件的記憶體空間  2、ctorinstance()初始化物件 3、instance = memory 設定instance指向剛分配的記憶體, 但是由於jvm和cpu的優化,會發生指令重排,如重排的結果變為1,3,2,在單執行緒的情況下沒有任何問題,但是在多執行緒的情況下就可能發生問題,如果此時a執行緒執行到instance=new singletonexample4(),發生了指令重排,執行到了第二步的3,此時instance已經執行了該物件的記憶體,但是該物件還沒有初始化,如果在此時b執行緒正好執行到if(instance==null),此時該條件已經不成立,直接return,因為這個物件還沒有初始化,直接去使用這個物件就可能發生問題。

/**

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

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

* 雙重檢測機制不一定執行緒安全,因為有指令重排的存在 */

public

class

singletonexample5

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

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

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

//通過volatile和雙重檢測機制限制指令重排,volatile限制了**的寫操作

//單例物件,通過volatile限制**發生指令重排

private

volatile

static singletonexample5 instance = null

;

//靜態的工廠方法

public

static

singletonexample5 getinstance() }}

return

instance;

}}

此demo是demo4的公升級版,只要解決了指令重排問題,在上篇部落格「執行緒安全性中」我們已經介紹了volatile可以限制**發生指令重排,此demo是執行緒安全的。

/**

* 餓漢模式

* 單例例項在類裝載時進行建立 */

@threadsafe

public

class

singletonexample6

//單例物件 靜態域的初始化

private

static singletonexample6 instance = null

;

//靜態塊方式

static

//靜態的工廠方法

public

static

singletonexample6 getinstance()

public

static

void

main(string args)

}

demo2是餓漢模式的靜態**域方式,此demo是餓漢模式的靜態**塊方式,此demo也是執行緒安全的

/**

* 列舉模式:最安全

* 相比於懶漢模式在安全性方面更容易保證

* 相比於餓漢模式是在實際呼叫的時候才做最開始的初始化 */

public

class

singletonexample7

//靜態的工廠方法

public

static

singletonexample7 getinstance()

private

enum

singleton

public

singletonexample7 getsingleton()

}}

此demo是我們最推薦的單例寫法,並且是執行緒安全的,它相比於懶漢模式在安全性方面更容易保證,相比於餓漢模式是在實際呼叫的時候才做最開始的初始化

併發程式設計 08安全發布物件之發布與逸出

小結 發布物件 使乙個物件能夠被當前範圍之外的 所使用,日常開發中比較常見的比如通過類的非私有方法返回物件的引用,或者通過公有的靜態變數發布物件等都屬於發布物件 物件逸出 首先需要明確的是物件逸出是一種錯誤的發布方式。當乙個物件還沒有構造完成時,就使它被其他執行緒所見。package com.art...

併發學習(十) 安全物件發布

平時我們建立的物件往往不會考慮到安全物件的概念,這可能比較陌生,但是你面試的時候面試官很喜歡問你執行緒安全的單例模式,而這就是相關的知識點 發布 使物件能夠在除了當前作用域之外的地方使用 最常用的方法 將物件的引用儲存到乙個公有的靜態變數中,讓任何類和執行緒都能看到該物件。逸出 某個物件不應該被發布...

(二)Java併發學習筆記 安全發布物件

上邊關於逸出的概念講述的很是模糊,下面列舉幾個逸出的示例。通過靜態變數引用逸出 public static setknownsecrets public void initialize 上邊 示例中,呼叫initialize方法,發布了knowsecrets物件。當你向knowsecrets中新增乙...