2012-02-27 71 views
62
List<String> list = Collections.synchronizedList(new ArrayList<String>()); 
synchronized (list) { 
    list.add("message"); 
} 

塊「synchronized(list){}」真的需要在這裏嗎?Collections.synchronizedList和同步

回答

19

底層代碼Collections.synchronizedList添加方法是:

public void add(int index, E element) { 
    synchronized (mutex) {list.add(index, element);} 
} 

因此,在您例如,它不需要添加同步。

+0

它將使用什麼樣的互斥體? – anoopelias 2013-11-07 10:48:47

+2

互斥量是javadoc中記錄的集合本身(this)。 – assylias 2013-11-07 12:24:03

89

您不需要按照您的示例進行同步。然而,非常重要的,你需要的時候你迭代它(如在Javadoc注)周圍列表同步:

當務之急是用戶迭代它時,返回 名單上手動同步:

List list = Collections.synchronizedList(new ArrayList()); 
... 
synchronized(list) { 
    Iterator i = list.iterator(); // Must be in synchronized block 
    while (i.hasNext()) 
     foo(i.next()); 
} 
+12

鏈接到上述語句:[docs](http://docs.oracle.com/javase/7/docs/api/java/util/Collections.html#synchronizedList(java.util.List)) – 2013-02-13 09:28:05

+1

常見用法像這樣的一個同步集合的情況是添加到多個線程的列表中,但只在所有任務完成時纔在最後進行迭代。 在這種情況下,我沒有看到任何理由在迭代周圍進行同步,因爲它是從單個線程完成的。 – Desty 2013-08-06 12:30:43

+3

只要您知道在迭代過程中列表未被更新,您就不需要同步它。不過,我不知道我是否會將該用例描述爲「普通」。 (我曾經見過ConcurrentModificationException不止一次。)在你提到的用例中,當另一個線程正在迭代時,什麼阻止線程再次添加到列表中?當其他線程完成更新列表時,迭代線程如何「知道」? – 2013-08-06 13:50:55

28

這取決於​​塊的確切內容:

  1. 如果塊執行在列表中是一個單一的原子操作(如你的例子),​​是多餘的。

  2. 如果塊執行列表上的多個操作 - 和需要保持的鎖的化合物操作的持續時間 - 那麼​​是多餘的。一個常見的例子是迭代列表。

16

同樣重要的是要注意,任何使用迭代器(例如Collections.sort())的方法也需要封裝在同步塊中。

+0

,排序方法已添加到List接口,並且此方法已在互斥鎖上同步。 https://docs.oracle.com/javase/8/docs/api/java/util/List.html#sort-java.util.Comparator- – 2017-12-09 16:38:47

7

閱讀本Oracle Doc

它說:「當務之急是用戶迭代它時,返回的列表上手動同步」

+1

這個文檔聽起來更像是一個教條而不是解釋。這就是我找到這個討論的原因。 – beemaster 2016-12-02 08:57:42

0

什麼樣的事情已被他人所提到的,同步的集合是thread-安全,但對這些集合的複合操作默認情況下不保證是線程安全的。

據JCIP,與普通複合動作可以

  • 迭代
  • 導航
  • 把-IF-缺席
  • 檢查當時的行爲

的OP的同步代碼塊不是複合動作,所以無論添加與否都沒有區別。

我們以JCIP爲例,對其進行一些修改,以闡明爲什麼有必要通過鎖來保護複合動作。

有兩種方法是在同一個集合list操作,通過Collections.synchronizedList

public Object getLast(List<String> list){ 
    int lastIndex = list.size() - 1; 
    return list.get(lastIndex); 
} 

public void deleteLast(List<String> list){ 
    int lastIndex = list.size() - 1; 
    list.remove(lastIndex); 
} 

包裹如果方法getLastdeleteLast是由兩個不同的線程調用的同時,下面的交錯可能發生,getLast將拋出ArrayIndexOutOfBoundsException 。假定當前lastIndex是10.

線程A(deleteLast) - >除去
線程B(getLast)-------------------->得到

線程A remove線程B在get操作之前的元素。因此,線程B仍然使用10作爲lastIndex來調用list.get方法,這會導致併發問題。