![](/img/trans.png)
[英]iterating over sorted 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 上显式同步(在本例中,似乎是序列化)。
理论上,即使使用同步的 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 的途径。 它仍然是有序的,自然地或通过比较器,并且访问和修改时间对于ConcurrentSkipListMap
和TreeMap
- 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.