2016-02-29 88 views
0

假設我有兩個線程正在更新一個對象,並且有一個線程正在讀取該對象而沒有同步。顯然,這是運行條件。但是,我想知道變量本身是否只能部分寫入。java變量本身是否線程安全?何時更新變量?

public class CommonObject extends Object 
{ 
    static int memberVar=-1; 
} 

public class Input1Thread extends Thread 
{ 
    public void run() 
    { 
     while(true) 
      CommonObject.memberVar = 1 
    } 
} 

public class Input2Thread extends Thread 
{ 
    public void run() 
    { 
     while(true) 
      CommonObject.memberVar = 2; 
    } 
} 

public class OutputThread extends Thread 
{ 
    public void run() 
    { 
     while(true) 
      System.out.println("CommonObject.memberVar"+ CommonObject.memberVar); 
    } 
} 

我假設打印出也將是2或1。然而,我想知道是否有可能的是,變量可能被中途設置的值?

我使用原語作爲例子,但我想對象的答案也是如果它不同。

+0

我覺得這個問題和答案都更好。他們也更專注於文字撕裂。 –

回答

3

它取決於變量的類型。

double S和long S(在Java中的兩個64位類型)允許字撕如果他們不volatile,而其他所有類型(包括引用)可能永遠不會撕裂。字撕裂會給你你擔心的行爲:一些字節來自舊值,其中一些來自新值,並且總體結果是一個既不舊又不新的值。

這在JLS 17.7指定:

對於Java編程語言存儲器模型的目的,對非易失性長或雙值的單個寫入被視爲兩個單獨的寫操作:一個每32一半。這可能會導致線程看到來自一次寫入的64位值的前32位和來自另一次寫入的第二次32位。

對volatile和double值的寫入和讀取總是原子的。

寫入和讀取引用始終是原子的,無論它們是以32位還是64位值實現的。

當然,介紹數據競賽引入了大量的問題;但你的問題是專門針對文字撕裂的,所以我只是在這裏解決這個問題,除了要注意「僅僅因爲你可以,並不意味着你應該。」你應該小心地分析你的每個數據競賽,並且證明它是良性的(因爲它們中的一些是 - 就像String.hashCode緩存它的值)。

+0

很好的答案。我認爲這個答案比其他問題的答案要好。 –

+0

因此,如果波動很大,那麼多長時間是完全安全的? –

+0

@GC_這取決於你的意思是「安全」!它不會流淚......但是數據競賽還有其他問題,比如看起來不按順序發生,或者根本不會被其他線程看到(如果它們被緩存在本地寄存器中)。 – yshavit

-1

它是原語的安全,但不能安全Objects.for例如,對象A有兩個變量的INT A,B,如果你試圖改變在兩個不同的線程他們的價值觀,你會發現,從價值有時兩個線程可能同時出現。

+0

對象不是變量。這個問題具體是關於變量。此外,對於_all_基元類型也不安全。 JLS明確允許'double'變量和'long'變量的更新是非原子的。 (見yshavit的答案) –