2010-10-03 93 views
1

我讀到以下類不是線程安全的,因爲線程可能讀取不一致的數據,因爲線程有機會讀取real的縮放版本和imaginary的未縮放版本。但我不知道如何。Java:使用同步方法的類中的線程安全

我的印象是,如果一個線程獲取的鎖,並在scale()方法,沒有其他的線程可以在同一時間在getReal()getImaginary()方法,使其他線程不能讀「半比例」的複數。這不正確嗎?

class Complex 
    { 
     double real; 
     double imaginary; 

     synchronized void scale(double scaleFactor) 
     { 
      real = real * scaleFactor; 
      imaginary = imaginary * scaleFactor; 
     } 

     synchronized double getReal() 
     { 
       return real; 
     } 

     synchronized double getImaginary() 
     { 
       return imaginary; 
     } 
    } 

回答

5

考慮以下情形:

  1. 線程A調用getReal()
  2. 線程B調用scale()
  3. 線程A調用getImaginary()

這樣線程A確實能得到不一致的實部和虛值。

的解決方案將是要麼

  • 創建一個公共同步getter方法立刻返回兩者值,或
  • 使類不可變的,如費雯麗建議。
2

沒有其他線程可以在getReal()或getImaginary()在同一時間的方法,以便其他線程不能讀取「一半縮放」複數。這不正確嗎?

是的,這是正確的,但是......

道格拉斯指出,需要訪問的實部和虛部必須執行兩個單獨的呼叫任何客戶端:一個real()和一個imaginary() (其中另一個線程可以在其間調用scale)。您沒有數據競賽,但行爲可能仍然取決於計劃。

此外,您需要使字段專用,否則,其他子類或同一包中的類可能會看到「半更新」的複數。

4

不是一個真正的直接答案,但在你的情況下,最好的選擇是讓你的類不可變。初始化後,Complex的每個實例都不能更改。

在這種情況下,您的比例方法將使用新值創建並返回一個新的Complex對象。

請注意,這是所有JVM Number類型的工作原理。

+1

爲了防止您的答案顯而易見,我會補充說,同步關鍵字應該被刪除,因爲不可變性不需要鎖。 – alpian 2010-10-03 21:08:09

2

如果您想要對兩部分進行計算,您班級的任何客戶都必須撥打getReal()然後getImaginary()

這些調用可以圍繞來自另一個線程的對scale()的調用。

也許最好的解決方案是使Complex類不可變,否則你最終不得不使其他位複雜化,以鎖定對象的鎖定&。