2011-10-03 27 views
0

嗨,我正在運行一個線程服務,此線程的任務是檢查HashMap中的列表項的年齡。當一件物品比5秒鐘還舊時,我將不得不從HashMap中刪除物品。以下是簡化代碼。但是,當代碼嘗試從HashMap中刪除項目時,我得到java.util.ConcurrentModificationException在修改線程類中的HashMap時獲取ConcurrentModificationException

我在原始程序中的main()方法中填充了HashMap。 有人可以幫我解決這個問題嗎?
PS:deleteFromTrackList()由不同的客戶端通過RMI通過網絡進行調用。

import java.util.*; 

public class NotifierThread extends Thread { 

    private HashMap<Integer, ArrayList> NotificationTrackList = new HashMap<Integer, ArrayList>(); 

    @Override 
    public void run() { 
     while (true) { // this process should run continuously 
      checkNotifierList(getNotificationTrackList()); 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    public HashMap<Integer, ArrayList> getNotificationTrackList() { 
     return NotificationTrackList; 
    } 

    public void deleteFromTrackList(Integer messageID) { 
     NotificationTrackList.remove(messageID); 
    } 

    public synchronized void checkNotifierList(HashMap list) { 

     Set entries = list.entrySet(); 

     for (Iterator iterator = entries.iterator(); iterator.hasNext();) { 
      Map.Entry<Integer, ArrayList> entry = (Map.Entry) iterator.next(); 

      ArrayList messageInfo = entry.getValue(); 
      Integer messageID = entry.getKey(); 

      messageInfo = new ArrayList((ArrayList) list.get(messageID)); 
      Long curTime = new Date().getTime(); 
      Long refTime = (Long) messageInfo.get(1); 
      Long timeDiff = curTime - refTime; 

      if (timeDiff > 5000) { 
       // delete the entry if its older than 5 milliseconds and update 
       // internal entry list 
       deleteFromTrackList(messageID); 
      } 
     } 
    } 

    public static void main(String[] args) { 
     new NotifierThread().start(); 
    } 
} 

這是我在控制檯

Exception in thread "tracker" java.util.ConcurrentModificationException 
    at java.util.HashMap$HashIterator.nextEntry(Unknown Source) 
    at java.util.HashMap$EntryIterator.next(Unknown Source) 
    at java.util.HashMap$EntryIterator.next(Unknown Source) 
    at NotifierThread.checkNotifierList(NotifierThread.java:32) 
    at NotifierThread.run(NotifierThread.java:10) 

回答

0

謝謝您的回答傢伙...我已經找到了我的問題的解決,而不是使用HashMap,我使用ConcurrentHashMap。這解決了我的問題。再次感謝 !

4

刪除從地圖中的條目的唯一途徑獲得,而遍歷它是使用迭代刪除它的堆棧跟蹤。使用

iterator.remove(); 

,而不是

deleteFromTrackList(messageID); 

注意,同樣適用於所有的集合(列表,設置等)

另外,請注意你的設計是不是線程安全的,因爲你讓其他線程以非同步的方式訪問地圖。

+0

我試過'iterator.remove();'但我需要刪除循環以外的項目,因爲'deleteFromTrackList(messageID);'由不同的客戶端通過RMI跨網絡調用。 – Rakesh

+1

事實上,您需要有一個單獨的方法來使其他客戶端能夠刪除消息ID,但這並不妨礙您在迭代時刪除迭代器時使用iterator.remove。在迭代列表時,您應該確保沒有其他線程調用deleteFromTrackList。不要讓地圖逃離班級,並同步每個地圖的訪問權限。 –

0

正確。您無法直接使用迭代器,而是在迭代它的同時修改Map。有幾個主要選項。

  1. 創建應刪除的元素列表。將每個過期的元素添加到循環中的列表中。循環後,從地圖中刪除列表中的元素。

  2. 使用番石榴的過濾能力。 Maps.filterEntries然而,這會創建一個新的地圖,但這對您嘗試執行的操作可能不起作用。

由於您有一個多線程系統。你可能想考慮作爲你朋友的不變性。您可以使用ImmutableMap,而不是在您的整個檢查過程中阻塞線程,這將更加線程安全並具有更好的性能。

+0

我必須在完成循環之前修改列表。有沒有辦法做到這一點?同時我會檢查番石榴的過濾能力。謝謝。 – Rakesh

+0

使用Iterator.remove方法。你確定你需要從循環中刪除嗎?根據你的同步,沒有其他線程可以訪問你正在檢查的地圖,所以我會認爲我無所謂。 –

0

您的代碼沒有完全同步。嘗試改變

public void deleteFromTrackList(Integer messageID) { 

public synchronized void deleteFromTrackList(Integer messageID) { 
+1

這可能是一件好事,但它不會修復'ConcurrentModificationException' –

+1

並且方法getNotificationTrackList()也必須被移除(或修改爲同步並返回映射的副本)以使其線程-安全 –

0

實際上,您甚至不需要併發訪問哈希映射來獲取併發異常。實際上,單個線程就夠了。

例如, 您可以基於散列圖map.keySet上創建一個循環()。迭代器(), 而且,當你在這個循環中,你的(單)線程決定刪除從一個元素哈希映射。 (迭代器打開時不是一個好主意。) 在迭代器()。next()的下一個請求中,您將獲得併發異常。

對此非常小心。

相關問題