2014-03-07 93 views
0

我知道在一個與多線程一起工作的程序中,有必要對這些方法進行同步,因爲它可能存在類似競爭條件的問題。
但我不明白爲什麼我們需要同步只需要讀取共享變量的方法。
請看下面的例子:
多線程:鎖定獲取並設置

public ConcurrentIntegerArray(final int size) { 
    arr = new int[size]; 
} 

public void set(final int index, final int value) { 
    lock.lock(); 
    try { 
     arr[index] = value; 
    } finally { 
     lock.unlock(); 
    } 
} 

public int get(final int index) { 
    lock.lock(); 
    try { 
     return arr[index]; 
    } finally { 
     lock.unlock(); 
    } 
} 


他們做的get,也對set方法看看。
在設置的方法我明白爲什麼。例如,如果我想把Thread1放在索引= 3的數字5和幾毫秒後,Thread2必須放入索引= 3的數字6.

這是否可以發生,我在索引= 3在我的數組仍然是一個5而不是6(如果我沒有在方法集上進行同步)?

這是因爲Thread1可以有一個開關上下文,所以Thread2在同一個方法中輸入值,並且Thread1在同一個位置上賦值5因此而不是6我有一個5.


但是我不明白爲什麼我們需要(看例子)同步方法get。
我在問這個問題,因爲我們只需要讀取內存,而不是寫入。

那麼爲什麼我們還需要在方法上進行同步?有人能給我一個非常簡單的例子嗎?

+0

請同時參閱[AtomicIntegerArray](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicIntegerArray.html) – TwoThe

回答

0

兩種方法都需要同步。如果沒有對get方法同步,這個序列是可能的:

  1. get被調用,但舊的價值還沒有返回。
  2. 另一個線程調用set並更新該值。
  3. 調用get的第一個線程現在檢查現在返回的值並查看現在的過期值。

同步將通過確保另一個線程不能只叫set之前甚至返回無效get值不允許這種情況。它會強制一個調用set的線程等待調用get的線程完成。

+0

您似乎在說,鎖定可以防止一種交互,其中set調用可以在get讀取值和返回之間完成。這看起來並不是一個很好的理由,因爲從技術上講,這仍然可能發生在鎖上。 – Radiodef

+0

@Radiodef這個鎖會阻止'set'方法在get方法獲取它的時候改變它的值。一旦'get'返回了值並放棄了鎖定,那麼當然''set''改變這個值當然沒問題。 – rgettman

+0

如果我明白你的意思是,如果我不把鎖還放在設置方法上,我可以「丟失」一些信息?例如:在索引3中的數組我有一個5,我調用get方法來獲取此值(5),但另一個線程之前讀取此值...在set方法和索引3輸入了10 ...所以在獲得方法後,我回來了10而不是5 ...我的解釋是正確的? –

0

如果不鎖定get方法,那麼線程可能會保留數組的本地副本,並且永遠不會從主內存刷新。所以它有可能一個get從來不會看到一個由set方法更新的值。鎖將強制顯示。

+0

您的意思是:可見度? –

0

每個線程都維護自己的值副本。同步確保了不同線程之間的一致性。如果沒有同步,人們無法確定是否有人修改了它。或者,可以將變量定義爲易失性,並且它將具有與同步相同的記憶效果。

0

鎖定動作還保證了內存的可見性。從Lock doc

所有鎖定實現必須執行相同內存同步語義提供的內置監視器鎖定,[...]:

  • 成功操作具有相同的存儲器同步效應作爲一個成功的動作。

  • 成功解鎖操作具有相同的存儲器同步效應作爲一個成功的解鎖動作。

沒有獲取鎖,由於memory consistency errors,沒有理由get通話需要看到最新的值。現代處理器速度非常快,對DRAM的訪問速度相對較慢,因此處理器會將其正在處理的值存儲在本地緩存中。在併發編程中,這意味着一個線程可能會向內存中的變量寫入數據,但隨後從另一個線程讀取的數據會因爲從緩存中讀取而變爲陳舊值。

鎖定保證該值實際上是從內存中讀取的,而不是從緩存中讀取的。