2011-12-13 40 views
2

我試圖提高對​​調用期間發出的鎖定範圍的理解。瞭解同步的行爲

如:

class CopyOnReadList<T> { 

    private final List<T> items = new ArrayList<T>(); 

    public void add(T item) { 
     items.add(item); 
    } 

    public List<T> makeSnapshot() { 
     List<T> copy = new ArrayList<T>(); 
     synchronized (items) { 
      // Make a copy while holding the lock. 
      for (T t : items) copy.add(t); 
     } 
     return copy; 
    } 

} 

(代碼精心來自this excellent answer借來的)

在此代碼段,可以一個線程調用add而另一個叫makeSnapshot?即,由synchronized (items)創建的鎖是否會影響所有嘗試讀取到items,或只有通過makeSnapshot()方法嘗試的讀取?

原崗位實際使用Add方法synchonized鎖:

public void add(T item) { 
    synchronized (items) { 
     // Add item while holding the lock. 
     items.add(item); 
    } 
} 

什麼是消除這種副作用?

+0

如果您從* add *方法中刪除* synchronized *,那麼當您調用* makeSnapshot *時,您根本無法保證其他線程添加的所有項目都具有「快照」。因此,據我可以看到,消除* synchronized *調用的副作用是現在代碼被破壞了。除此之外,甚至有可能達到不一致的狀態...... –

回答

2

它僅影響makeSnapshot()或更一般地說已經同步(項目)塊的任何其他方法(這意味着它將嘗試獲取項目對象和塊的鎖定直到有可能)。

從add()方法中刪除同步塊的副作用是add()不會嘗試同步項目對象,因此將允許併發修改,包括執行makeSnapshot()時的同時修改。

如果沒有在add()中同步,您可以讓其他線程將元素添加到項目集合中。

1

在這個代碼片段,可以在一個線程調用添加另一種就是調用 makeSnapshot?

當然 - 並且其中一種方法可能會因ConcurrentModificationException失敗,或者列表內容可能已損壞。

並通過同步(項目)中創建的鎖影響所有試圖 讀取到的物品,或者那些僅通過makeSnapshot() 方法嘗試?

都沒有。該鎖對對象items的行爲沒有任何影響,僅限於在其上同步的塊或方法 - 即確保沒有兩個線程可以同時執行任何這些塊或方法。

1

可以一個線程調用add而另一個調用makeSnapshot?

是的。​​確保任何其他線程無法輸入同一個對象(本例中爲CopyOnReadList)的另一個同步代碼塊。由於您沒有同步add方法,因此即使一個線程正在執行makeSnapshot,多個線程也可以同時調用add

通過刪除同步的add方法,您已使代碼無線程安全,因爲ArrayList不是線程安全的。

經驗法則是:對共享可變狀態的每次訪問(讀或寫)必須在同一個鎖上進行同步。