Java并发和仅添加哈希映射

Java并发和仅添加哈希映射,java,multithreading,locking,hashmap,Java,Multithreading,Locking,Hashmap,我正在编写一个广泛使用大型哈希映射的程序。它是多线程的,所以我在访问它时使用了读写锁。然而,它有一个特殊的属性,我想利用 将数据“放入”HashMap后,该数据永远不会更改。曾经每当对此数据结构的状态进行更改时,实际上只会创建新的“一代”结构,而保留旧的结构 也就是说,在另一个线程正在写入值的同时从HashMap读取值是否安全,因为知道另一个线程永远不会写入您正在读取的值?有没有一些简单的哈希表结构可以给我这种保证?没有。因为您可以对它进行写入,所以在写入时可能会触发调整基础数组的大小。如果你在

我正在编写一个广泛使用大型哈希映射的程序。它是多线程的,所以我在访问它时使用了读写锁。然而,它有一个特殊的属性,我想利用

将数据“放入”HashMap后,该数据永远不会更改。曾经每当对此数据结构的状态进行更改时,实际上只会创建新的“一代”结构,而保留旧的结构


也就是说,在另一个线程正在写入值的同时从HashMap读取值是否安全,因为知道另一个线程永远不会写入您正在读取的值?有没有一些简单的哈希表结构可以给我这种保证?

没有。因为您可以对它进行写入,所以在写入时可能会触发调整基础数组的大小。如果你在另一个线程的读取中间触发一个调整大小,你就真的会被它精确地找到数据的能力搞糊涂了!p> 问题不在于hashmap中的数据,而是在插入内容时修改了hashmap本身;它的结构。使用标准HashMap不能同时使用多个线程

java并发包确实提供了线程安全hashtmap:


在内部,这将使用线程安全的非锁定方法。

我知道您已经声明它不会被覆盖,但值得考虑使用ConcurrentHashMap,因为您不再需要“锁定”代码

这个特殊的映射(自java 1.5以来)保证您永远不会得到ConcurrentModificationException,因为它将返回您最后一次“完成”写入

对于多个并发读取,它的速度也非常快。有关更多信息,请参阅本文:

需要注意的其他事项:它不允许空键/值,并且它还有另一个方便的方法,putIfAbsent


HTH

您可以使用persistentMap来代替HashMap,然后每个编写器都必须在添加新对象并用新对象替换对映射的引用时锁定它,但是读者始终可以读取“当前”版本(可能找不到他们正在查找的值,因为它是同时添加的)


请注意,读取和写入对地图的引用必须以原子方式进行。

他已经声明他没有向地图写入内容。没有理由为只读锁定/同步。他不是这么说的。他说他没有覆盖地图。假设他添加了
对象eo
。然后他对地图进行了更改。而不是覆盖
eo
在地图中,他将改为
EthanObject eo2
并添加
eo2
。不,我正在向HashMap写入,只是写入的值与我正在读取的值不同。我明白glowcoder的意思-你能推荐一种数据结构,它可以优雅地调整大小,这样不会破坏当前读取的逻辑吗?@Ethan Unform当然,我不能。但是,编写自己的数组并不是不可想象的(或困难的)。问题是它将涉及底层数组结构的副本,使
insert()
成为
O(n)
call.@Ethan-在以某种方式修改hashmap后,是否要对其进行深度复制?这些新副本是只读的,不需要@Brian Roach指出的MT sync。但是,为了选择正确的“版本”,您可能需要进行同步阅读。或者,可能我遗漏了什么……我认为并发哈希映射同时使用了锁定和非锁定方法来保证线程安全。锁定方法不锁定整个映射,而是锁定映射的一部分。@richs-我必须阅读源代码,但大多数并发包依赖于CAS而不是锁,javadocs说即使如此所有操作都是线程安全的,检索操作不需要锁定,并且不支持以阻止所有访问的方式锁定整个表。两个并发插入可能会涉及锁定,但总体而言,我认为对于他来说,使用重量级锁进行所有访问的开销应该更少。