2012-09-16 45 views
2

我想通過一個循環來遍歷一個線程,就像這樣:同步在每個循環仍然拋出ConcurrentModificationExceptions

for (UnitTask task : chain) { 
    g.drawLine((int) task.getLocation().getX(), (int) task.getLocation().getY(), (int) currentPos.getX(), (int) currentPos.getY()); 
    g.fillOval((int) task.getLocation().getX() - 2, (int) task.getLocation().getY() - 2, 5, 5); 
    currentPos = task.getLocation(); 
} 

不過,我還有一個線程(Swing事件線程),可在其中加到這個對象。因此,ConcurrentModificationException。我試圖通過圍繞代碼synchronized (chain) { ... }獲得鎖,但我仍然得到錯誤。

帶有一點Java同步新手,我爲何有點困惑。我希望這可以使循環線程安全,但顯然它不是。

有趣的是,chain是一個自定義類的實例,但它只是一個圍繞LinkedList的薄包裝。列表本身是私有的,外部類無法直接檢索它(有些方法可以顯式添加/刪除對象),所以我不希望這會影響結果。

+0

因此,對於以'for(UnitTask task:...')開頭的代碼行,即使整個for循環被封裝在'synchronized(chain){...}'中,也會引發ConcurrentModificationException? –

+0

@ RayToal是的,這是正確的。 –

+0

您是否在其他線程中同步列表? –

回答

10

synchronized (c) { 
    ... code that uses c ... 
} 

含義是

  • 等待c被解鎖
  • c
  • 執行主體
  • 解鎖c

所以,如果你在你的線程同步,那麼你的線程將等待c被解鎖,然後俯衝。

現在,如果你代碼的其他線程修改同步c,該代碼將繼續並修改c而不等待鎖定。在一個線程中同步塊不會使另一個線程等待鎖定。如果另一個線程有一條線,如

c.add(someOtherTask) 

不在同步塊中,那麼無論如何它都會進行添加。這是你例外的原因。這也是爲什麼即使將線程中的代碼放入同步塊中也會看到異常的原因:您的代碼「按規則進行遊戲」,但其他線程無法減少關注。

但要小心同步長時間運行的代碼。正如Stephen C所說,您最好使用併發收集類型。

+1

謝謝,這解釋了很多。我最終選擇了[ConcurrentLinkedQueue](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html),因爲它速度更快,並且在Java 6中得到了支持。不過,我很高興現在至少能夠理解Java的鎖定系統。 –

+0

...和CLQ不投擲CME。 ;) –

3

同步不一定有幫助。

基本上問題是,您正在使用的集合類型不允許在迭代進行時修改集合(除非通過迭代器的remove方法...如果支持)。這不是線程/同步問題本身。 (如果你試圖簡單地通過同步來解決它,你可能會引入另一個問題。)

如果你想能夠同時迭代和修改,你將需要使用不同的集合類型,如ConcurrentLinkedDeque而不是LinkedList


如果迭代和寫入單獨的線程上發生的事情,那麼應該不會同步塊的寫入,直到迭代結束?或者我錯過了什麼?

的問題會在你是如何實現的同步:

  • 如果你沒有明確做你LinkedList版本某種同步,則不會同步爲你做了。

  • 如果您使用的Collections.synchronizedXxx方法之一創建一個同步的包裝,然後對這些方法的javadoc明確指出,由包裝的iterator()方法返回一個Iterator對象是不同步的。

  • 如果你正在做手工同步,那麼你必須確保一切都在同一個互斥體同步。並且該鎖必須在迭代期間保持在該互斥鎖上,而不僅僅是呼叫iterator()

請注意,如果你持有很長一段時間(例如,當您正在迭代一個長長的清單)的鎖,這將有可能阻止需要更新列表很長一段時間其他線程。這種事情可能是一個併發瓶頸,可以(在最壞的情況下)將系統的性能降低到單個處理器的速度。

ConcurrentXxx類通常通過放鬆對迭代器產生的序列的一致性保證來避免這種情況。例如,在開始迭代之後,您可能看不到添加到集合中的元素。

+0

如果迭代和寫入發生在單獨的線程上,那麼不應該同步阻塞寫入,直到迭代完成?或者我錯過了什麼? –

相關問題