kuks
kuks

Reputation: 31

java.util.ConcurrentModificationException in a TreeMap

public class StoreMessage extends Thread implements Serializable{

    private static long start_nanotime=System.nanoTime();
    private static int timeToRun = 60000; 
    private static byte[] b=null;
    private static long startTime = System.currentTimeMillis();
    private static long runUntilMillis = System.currentTimeMillis() + timeToRun;
    public static Map <Long,Message> map1=new TreeMap<Long,Message>();

public static void store(Message message)throws Exception{
        while (true) {
            long now = System.currentTimeMillis();
            if (now >= runUntilMillis) {
               break;
            }
            long precise_time=TimeUnit.MILLISECONDS.toNanos(now)+(System.nanoTime()-start_nanotime);
            map1.put(precise_time, message);
           }
     }

public static byte[] returning()throws Exception
    { 

        b=serializer.serialize(map1);
        System.out.println(b);
        map1.clear();
        return b;


    }
}

What I am trying to do is, store all my message objects received by the class StoreMessage in every one minute to a TreeMap, serialize that TreeMap and return it to the class calling it, and create/clear the TreeMap for the next minute to store the other message objects.
Message object of the message class are jms text messages, which are input as command-line-arguments. The store method is called in another class, while the returning() method is called in a different class. Both classes, when instantiated and run, with more than one argument, give me an exception

java.util.ConcurrentModificationException
at java.util.TreeMap$PrivateEntryIterator.nextEntry(TreeMap.java:1100)
at java.util.TreeMap$EntryIterator.next(TreeMap.java:1136)
at java.util.TreeMap$EntryIterator.next(TreeMap.java:1131)
at java.util.TreeMap.writeObject(TreeMap.java:2250)

Why? Especially, when I am clearing the map. I do not get this exception, if I am giving only one command-line-argument. But if it is complied over and over,i get the same exception.
Secondly, I noticed that, as and when the message objects as received, they are stored into the TreeMap and serialized and returned. When I want the tree map to store the messages for one minute and then serialize the whole lot.

Upvotes: 0

Views: 8727

Answers (6)

Suraj Kamble
Suraj Kamble

Reputation: 11

Using ConcurrentSkipListMap instead of TreeMap worked for me.

Upvotes: 0

Dimitar
Dimitar

Reputation: 4801

A bit late for the party but still, as @Flinbor pointed out, ConcurrentSkipListMap is the way to go in such situation. It is still ordered, naturally or through Comparator, and the access and modification times are the same for both the ConcurrentSkipListMap and the TreeMap - O(log(n)). However, could be a bit slower due to synchronization.

While TreeMap's Iterator is fail-fast, meaning there was a structural modification post iteration creation by other than his own remove() method. While ConcurrentSkipListMap's Iterator is weakly consistent, returning elements reflecting the state of the map at some point at or since the creation of the iterator. They do not throw ConcurrentModificationException.

Upvotes: 0

Alexander B
Alexander B

Reputation: 3510

java.util.concurrent.ConcurrentSkipListMap is the implementation for Thread safe TreeMap and it will keep the natural ordering

Map<String, String> treeMap = new ConcurrentSkipListMap<String, String>();

We can obtain unmodifiable (read-only) version also as follows:

TreeMap tM = new TreeMap();
Map tM2 = Collections.unmodifiableMap(tm1);

Now the Map tM2 is read-only.

Upvotes: 4

user1161639
user1161639

Reputation: 31

In theory you could lose a message even with a synchronized map. If a store is called while the System.out.println() is in progress. That is after it is serialized but before it is cleared.

So I think you could synchronize on the map (but I didn't test it):

public static void store(Message message) throws Exception {
    while (true) {
        long now = System.currentTimeMillis();
        if (now >= runUntilMillis) {
            break;
        }
        long precise_time = TimeUnit.MILLISECONDS.toNanos(now)
                + (System.nanoTime() - start_nanotime);
        synchronized (map1) {
            map1.put(precise_time, message);
        }
    }
}

public static byte[] returning() throws Exception {

    synchronized (map1) {
        b = serializer.serialize(map1);
        System.out.println(b);
        map1.clear();
    }
    return b;
}

Upvotes: 0

James
James

Reputation: 8586

The other answers are close, but I don't believe they're complete. If you look at the CME being thrown, it's in a TreeMap iterator, which won't be made thread-safe even if you make it be a synchronizedMap. You still have to explicitly synchronize on the TreeMap during iteration (in this case, serialization, it seems).

Explain synchronization of collections when iterators are used?

Upvotes: 2

Alex Stybaev
Alex Stybaev

Reputation: 4693

try using Collections :

    public static Map <Long,Message> map1 = 
Collections.synchronizedMap(new TreeMap<Long,Message>());

to synchronize your Map instance.

Upvotes: -1

Related Questions