發布物件是指使乙個物件能夠被當前範圍之外的**所使用
物件逸出是一種錯誤的發布,指當乙個物件還沒有構造完成時,就使它被其他執行緒所見
@slf4jpublic
class
escape
private
class
innerclass", escape.this
.thiscanbeescape);}}
public
static
void
main(string args)
}
在此例項中escape物件還沒有構造完成,就訪問了該物件的成員變數thiscanbeescape,該類是執行緒不安全的,並且非常不推薦這麼寫。
@slf4jpublic
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、將物件的引用儲存到乙個由鎖保護的域中
我們以對不同單例的實現,來說明一下安全發布物件的方法
publicclass
singletonexample1
//單例物件
private
static singletonexample1 instance = null
;
//靜態的工廠方法
public
static
singletonexample1 getinstance()
return
instance;
}}
此例項在單執行緒模式下沒有任何問題,但在多執行緒模式下,如兩個執行緒都同時執行到判斷instance==null時,就有可能new出兩個例項來,所以說這是執行緒不安全的,這也就是懶漢模式,此例項滿足了a條件,如果再加上d條件,在判斷是否為null時加鎖,就可以變為執行緒安全的
publicclass
singletonexample2
//單例物件
private
static singletonexample2 instance = new
singletonexample2();
//靜態的工廠方法
public
static
singletonexample2 getinstance()
}
此demo是執行緒安全的,使用餓漢模式時需要注意兩點,一是此類肯定被使用(避免造成資源浪費),二是私有的構造方法中沒有過多的處理4
publicclass
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中新增乙...