[英]A concurrent issue among threads
假設我有一個具有原始值的實例變量:
Integer mMyInt = 1;
有兩個線程。
第一個通過調用以下命令更改mMyInt:
void setInt() {
mMyInt = 2;
}
第二個線程通過調用以下命令獲取mMyInt:
Integer getInt() {
return mMyInt;
}
兩個線程都不使用同步。
我的問題是,第二個線程可以從getInt()獲得什么可能的值? 只能是1或2嗎? 它可以為空嗎?
謝謝
編輯:重要更新由於@irreputable。
除非對象在構造過程中已逸出(請參見下文), mMyInt=1
分配mMyInt=1
會發生在對getter / setter的任何訪問之前。 同樣在Java中,對象分配是原子的(觀察到分配給無效地址的機會為0。請小心,因為64位基元分配(例如double
和long
都不是原子的))。
因此,在這種情況下,可能的值為1或2。
在這種情況下,對象可能在構造過程中逸出:
class Escape {
Integer mmyInt = 1;
Escape(){
new Thread(){
public void run(){
System.out.println(Escape.this.mmyInt);
}
}.start();
}
}
盡管在實踐中它很少會發生,但在上述情況下,新線程可以觀察到未完全構造的Escape
對象,因此從理論上講它的mmyInt
值為null
(AFAIK您仍然不會得到一些隨機的內存位置)。
如果它是HashMap對象怎么辦? 實例變量mMyMap具有原始值。 然后,第一個線程調用“ mMyMap = new HashMap();”。 第二個線程調用“ return mMyMap;”。 第二個線程可以獲取空值,還是只能獲取原始或新的HashMap對象?
當“對象引用分配是原子的”時,這意味着您將不會觀察到中間分配。 它是之前的值或之后的值。 因此,如果發生的唯一分配是map = someNonNullMap();
在構造完成之后(並且在構造期間為字段分配了一個非null值)並且在構造期間對象未逸出后,您將無法觀察到null
。
更新:我咨詢了一位並發專家,據他介紹,Java內存模型允許編譯器對分配和對象構造進行重新排序(而實際上,我認為這不太可能發生)。
因此,例如在下面的情況下,線程1可以分配一些堆,分配一些值map
,該繼續的結構map
。 同時thread2來觀察部分構造的對象。
class Clever {
Map map;
Map getMap(){
if(map==null){
map = deriveMap(); }
return map;
}
}
JDK在String
類中具有類似的構造(不完全引用):
class String {
int hashCode = 0;
public int hashCode(){
if(hashCode==0){
hashCode = deriveHashCode();
}
return hashCode;
}
}
根據同一位並發專家的說法,之所以起作用是因為非易失性緩存是原始緩存,而不是對象。
這些問題可以通過在關系發生之前發生來避免。 在上述情況下,可以通過聲明成員volatile
來做到這一點。 同樣對於64位基元,聲明它們為volatile
將使它們的分配成為原子。
// somewhere
static YourClass obj;
//thread 1
obj = new YourClass();
// thread 2
if(obj!=null)
obj.getInt();
從理論上講,線程2可以為空。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.