开发者

Getting ConcurrentModificationException while modifying a HashMap in a thread class

Hi I am runni开发者_JAVA百科ng a thread service, the job of this thread is to check the age of a list items in a HashMap. When an item is older than say 5 seconds, I will have to delete the item from the HashMap. The below is the simplified code. But when the code attempts to delete the item from the HashMap, I get a java.util.ConcurrentModificationException.

I am populating the HashMap in the main() method in the original program. Can somebody please help me out with this ?

PS: The deleteFromTrackList() is being called by different clients across a network through 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();
    }
}

This is the stacktrace I am getting at the console

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)


The only way to remove an entry from a map while iterating over it is to remove it using the iterator. Use

iterator.remove();

instead of

deleteFromTrackList(messageID);

Note that the same applies to all the collections (List, Set, etc.)

Also, note that your design is not thread-safe, because you let other threads access the map in an unsynchronized way.


Your code isn't complectly synchronized. Try to change

public void deleteFromTrackList(Integer messageID) {

to

public synchronized void deleteFromTrackList(Integer messageID) {


Correct. You cannot modify a Map while iterating over it without using the iterator directly. There are a couple main options.

  1. Create a List of elements that should be removed. Add each expired element to the List in the loop. After the loop, remove the elements in the List from the Map.

  2. Use Guava's filter capability. Maps.filterEntries This creates a new Map however which may not work for what you are trying to do.

Since you have a multi-threaded system. You may want to consider immutability as your friend. Rather than blocking threads over your entire check for stale loop, you could use an ImmutableMap which would be more thread-safe with better performance.


Thanks for your answers guys... I have found the solution for my question, instead of using HashMap, I am using ConcurrentHashMap. This solved my problem. Thanks again !


In reality you do not even need concurrent access to an hash map to get a concurrency exception. In fact, a single thread is quite enough.

For example, You may create a loop based on the hash map map.keySet().iterator(), And, while you are within this loop, your (single) thread decides to remove an element from the hash map. (Not a good idea while the iterator is open.) In the next request to the iterator().next() you will get your concurrency exception.

So careful with that.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜