繁体   English   中英

TreeMap 中的 java.util.ConcurrentModificationException

[英]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;


    }
}

我想要做的是,将 class StoreMessage 每隔一分钟接收到的所有消息对象存储到 TreeMap,序列化该 TreeMap 并将其返回给调用它的 class,并在下一分钟创建/清除 TreeMap 以存储其他消息对象。
消息class中的消息object为jms文本消息,作为command-line-arguments输入。 store方法在另一个 class 中被调用,而returning()方法在另一个 class 中被调用。这两个类在实例化和运行时,具有多个参数,给我一个异常

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)

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

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

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

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

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

现在 Map tM2是只读的。

其他答案很接近,但我不相信它们是完整的。 如果您查看抛出的 CME,它位于 TreeMap 迭代器中,即使您将其设为 synchronizedMap 也不会使其成为线程安全的。 您仍然必须在迭代期间在 TreeMap 上显式同步(在本例中,似乎是序列化)。

使用迭代器时解释collections的同步?

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

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

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

聚会有点晚了,但正如@Flinbor 指出的那样,在这种情况下, ConcurrentSkipListMap是通往 go 的途径。 它仍然是有序的,自然地或通过比较器,并且访问和修改时间对于ConcurrentSkipListMapTreeMap - O(log(n))是相同的。 但是,由于同步,可能会慢一些。

虽然 TreeMap 的 Iterator 是fail-fast ,这意味着除了他自己的remove()方法之外,还有一个结构修改后迭代创建。 虽然 ConcurrentSkipListMap 的迭代器是弱一致性的,但返回的元素反映了 map 的 state 在迭代器创建时或创建后的某个时间点。 它们不会抛出 ConcurrentModificationException。

使用ConcurrentSkipListMap而不是 TreeMap 对我有用。

尝试使用Collections

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

同步您的 Map 实例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM