簡體   English   中英

線程之間的並發問題

[英]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位基元分配(例如doublelong都不是原子的))。

因此,在這種情況下,可能的值為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM