2

我正在使用结构的并发哈希图

Map<Set<Date>, Map<String, Object>> SampleMap

在给定的 map ( Map<String, Object>) 中使用的 Map 也是一个并发的 hashmap,但 set 是唯一的TreeSet类型。

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

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

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

此映射在批处理过程中被多个线程广泛用于在映射中放置和删除值,使用的 java 版本是 1.5。

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

如果有人确认我对给定问题的思考是否正确,那将是很好的,也请建议解决这个问题?

4

1 回答 1

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 不支持的操作时,您必须使用自己的锁定。您可以使用带同步或 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 发生变化,它可能在错误的存储桶中,所以当它查找它时,它找不到它。

于 2012-09-10T11:32:35.977 回答