地图中的并发修改问题

时间:2012-09-10 11:27:40

标签: java concurrentmodification

我正在使用结构

并发hashmap
Map<Set<Date>, Map<String, Object>> SampleMap

在给定地图(Map<String, Object>)内使用的地图也是并发哈希地图, 但是set只是TreeSet类型。

当我在日志中添加以下行时,仍然会出现并发修改异常

logger.debug("sampleMap.keySet() + ". Size is " + sampleMap.keySet().size()"); 

以及处理此地图的同一类的其他部分。

这个地图在批处理过程中广泛用于多个线程来放置和删除map中的值,并且使用的java版本是1.5。

我认为异常是由Treeset引起的,而且我发现类型Set的并发处理集合没有类似的实现。

如果有人确认我对某个问题的看法是否正确并且请为此问题提出解决方案,那会很棒吗?

1 个答案:

答案 0 :(得分:3)

由于您需要能够“修改”密钥,因此您需要遵循此模式

// lock the collection
Map<String, Object> values = map.remove(key);
key = new TreeSet<String>(key);
// modify copy of key
map.put(key, values);
// unlock the collection.

当您执行ConcurrentMap不支持的操作时,您必须使用自己的锁定。您可以使用带有synchronized或ReentrantReadWriteLock的普通HashMap或LinkedHashMap。


您可以使用

创建并发集
// Added in Java 1.6
Set<String> set = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
// or to be sorted
Set<String> set = Collections.newSetFromMap(new ConcurrentSkipListMap<String, Boolean>());

但是,您无法更改密钥的内容,因此您应该使用的是

Set<String> key = Collections.unmodifiableSet(treeSet);
// or to be sure its not modified
Set<String> key = Collections.unmodifiableSet(new TreeSet<String>(treeSet));

一个简单的示例,说明在Map中使用密钥后无法更改密钥的原因。

Set<String> key1 = new TreeSet<String>();
Map<Set<String>, Boolean> map = new ConcurrentHashMap<Set<String>, Boolean>();
map.put(key1, true);
System.out.println("Is the map ok? "+map.containsKey(key1));
key1.add("hello");
System.out.println("Is the map ok? "+map.containsKey(key1));

打印

Is the map ok? true
Is the map ok? false

常见的行为是它无法再看到地图中的密钥。这是因为映射根据其hashCode将密钥放入存储桶。如果hashCode发生了变化,它可能在错误的桶中,所以当它找到它时,它找不到它。