2015-11-14 73 views
0

我有2類內部線程類Main。有時,如果在刪除另一個元素的同時添加新元素,則會導致ConcurrentModificationException異常。我想我不知道如何同步它們。ConcurrentModificationException當多個線程訪問相同集合

Class Main{ 
HashSet<MyObject> set; 
Thread A{ 
     run(running){ 
      ... 
      set.add(obj); 
      ... 
     } 
    } 

Thread B{ 
    run(){ 
     while (running) { 
       for (Iterator<MyObject> i = set.iterator(); i.hasNext();) { 
        MyObject obj= i.next(); 
        if (!obj.isSmt()) { 
         i.remove(); 
         ... 
        } 
       } 
      } 
    } 
} 

} 

回答

1

最簡單的解決方案是將讀碼從寫入代碼隔離。您可以通過使用synchronized(set)塊來修改這些修改。對於第一個電話,我們一定要圍繞增加通話同步:

run(running){ 
... 
    synchronized(set) { 
     set.add(obj); 
    } 
... 
} 

對於第二個電話,我們需要圍繞整個迭代同步,以避免併發修改。 i.remove()在單線程情況下是正確的,但正如您發現的,它不適用於多個線程。

synchronized(set) { 
    for (Iterator<MyObject> i = set.iterator(); i.hasNext();) { 
     MyObject obj= i.next(); 
     if (!obj.isSmt()) { 
      i.remove(); 
      ... 
     } 
    } 
} 

synchronized(set)是對象set上的鎖。只有一個線程能夠在給定的時間輸入任一個同步塊,從而防止在線程迭代時將項添加到該集。

+0

我相信語法是'synchronized(set ){...}'。 – Andreas

0

您不能在遍歷元素時修改元素列表。這將導致ConcurrentModificationException。如果要刪除元素,請將其存儲在臨時列表中,然後在迭代完成後刪除列表。

+1

這是不正確的( )'從集合中刪除項目。看到http://stackoverflow.com/questions/223918/iterating-through-a-list-avoiding-concurrentmodificationexception-when-removing –

1
  1. 使用集合的副本刪除項目,您不能刪除項目時,迭代收集。

  2. 要同步使用Lock() over collection。

    Lock myLock= new Lock(); 
    myLock.lock(); 
    set.add(item); 
    myLock.unlock(); 
    
    myLock.lock(); 
    ...while loop and modification.. 
    myLock.unlock(); 
    
+0

我已經試過這個,但它造成了死鎖 –

1

ConcurrentModificationException由ThreadA中的set.add(obj)引起,而迭代在ThreadB中進行(而不是由循環期間的set.remove())。

線程需要同步以避免這種情況。

線程在某些對象上使用內部鎖進行同步。您使用'同步'關鍵字聲明:

// entire method synchronized on 'this' 
synchronized SomeValue foo(); 

// block synchronized on obj: 
synchronized(obj) { 
    // stuff. 
} 

根據您需要同步的內容,細節差異很大。在集合的情況下,隔離add()或remove()等特定操作通常是安全的,但是如果需要遍歷集合中的元素,則需要同步整個將進行迭代的塊使用普通的集合實現:

synchronized(set) { 
    for (Iterator<MyObject> i = set.iterator(); i.hasNext();) { 
     ... 
    } 
} 

然而,通常可以實現更高效的同步取決於集合的性質,這是很容易通過手工同步時犯錯誤。在大多數情況下,通常最好只使用java.util.concurrent中的一個集合實現,它實現了線程安全的迭代器,並且不會從其他線程的操作中拋出ConcurrentModificationException。

出於某種原因,沒有ConcurrentHashSet實現集,但也可以通過使用newSetFromMap獲得一個實例:因爲他正確地使用`i.remove

HashSet<MyObject> set = Collections.newSetFromMap(new ConcurrentHashMap<MyObject,Object>());