TreeMap中的java.util.ConcurrentModificationException

时间:2012-04-05 08:17:33

标签: java 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;


    }
}

我要做的是,将StoreMessage类每隔一分钟收到的所有消息对象存储到TreeMap中,序列化该TreeMap并将其返回给调用它的类,并为下一个创建/清除TreeMap分钟存储其他消息对象 消息类的消息对象是jms文本消息,它们作为命令行参数输入。 store方法在另一个类中调用,而returning方法在另一个类中调用。这两个类在实例化和运行时都有多个参数,给我一个例外

  

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

为什么呢?特别是,当我清理地图时。如果我只给出一个命令行参数,我不会得到这个例外。但如果一遍又一遍地遵守,我会得到同样的例外 其次,我注意到,当消息对象被接收时,它们被存储到TreeMap中并被序列化并返回。当我希望树图存储消息一分钟然后序列化整个批次时。

5 个答案:

答案 0 :(得分:2)

其他答案很接近,但我不相信它们是完整的。如果你看一下被抛出的CME,它就在一个TreeMap迭代器中,即使你把它作为一个synchronizedMap也不会成为线程安全的。您仍然必须在迭代期间在TreeMap上显式同步(在这种情况下,似乎是序列化)。

Explain synchronization of collections when iterators are used?

答案 1 :(得分:2)

java.util.concurrent.ConcurrentSkipListMap是线程安全TreeMap的实现,它将保持自然顺序

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

我们也可以获得不可修改的(只读)版本,如下所示:

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

现在地图tM2是只读的。

答案 2 :(得分:0)

理论上,即使使用同步地图,您也可能会丢失消息。如果在System.out.println()正在进行时调用商店。这是在序列化之后但在它被清除之前。

所以我认为你可以在地图上同步(但我没有测试它):

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;
}

答案 3 :(得分:0)

派对有点迟,但仍然,

正如@Flinbor指出的那样,ConcurrentSkipListMap是在这种情况下走的路。它仍然是自然地或通过比较器订购的,ConcurrentSkipListMapTreeMap - O(log(n))的访问和修改时间是相同的。但是,由于同步,可能会慢一点。

虽然TreeMap的Iterator是 fail-fast ,但意味着迭代创建后的结构修改不是他自己的 remove() 方法。而ConcurrentSkipListMap的迭代器是

  

弱一致,返回元素反映迭代器创建时或之后某点的映射状态。它们不会抛出ConcurrentModificationException。

答案 4 :(得分:-1)

尝试使用Collections

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

同步你的Map实例。