java哈希映射线程可见性

我在初始化时完全加载了一个java HashMap,但在初始化之后,多个线程将从HashMap读取数据。 我想避免任何类型的同步,因为地图基本上是只读的,永远不会改变。 但我可以保证所有线程都可以看到所有键和值吗?

如果地图的内容永远不会改变,那么您就没有问题。 只有当变量的内容发生变化时,内存模型可见性问题才会发挥作用。

您可能希望同步映射的初始化,以确保在完全初始化之前没有线程访问它,并确保加载到映射中的值都是可见的。

编辑:最初我完全忽略了地图如何初始化的问题。 在阅读了一篇Pugh文章之后 (再次),似乎地图确实需要最终才能使初始化数据变得可见:

能够查看字段的正确构造值是很好的,但如果字段本身是引用,那么您还希望代码查看它指向的对象(或数组)的最新值。 如果您的字段是最终字段,则也可以保证。 因此,您可以拥有一个指向数组的最终指针,而不必担心其他线程看到数组引用的正确值,但是数组内容的值不正确。 同样,在这里“正确”,我们的意思是“对象的构造函数结束时的最新”,而不是“可用的最新值”。

有一个强制“发生在之前”关系的条件列表,在Java规范中给出,我应该在这里引用它们(或者如果其他人在他们的答案中做了,我会投票给它)。 静态变量和Holder成语当然是一种方法。 问题非常广泛,因为它没有指定地图如何初始化,如果你发布一个描述你如何建议进行初始化的问题,你可能会得到一个更直接有用的答案。

如果您将HashMap声明为final,并且您预先初始化了一个本地HashMap,那么在构造函数初始化之后将全局HashMap与本地存储在一起,HashMap的内容可以被视为可见。

必须正确使用最终字段以提供不可变性的保证。 当构造函数完成时,对象被认为是完全初始化的。 在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值。

http://www.cs.umd.edu/~pugh/java/memoryModel/jsr133.pdf

使用Guava的ImmutableMap是最好的解决方案。

如果所有线程都只是读取它,则不需要同步映射。 为了确保不变性,我会在初始化后将Map转换为不可修改的Map:

 map = Collections.unmodifiableMap(map); 

如果线程调用将修改Map的操作,则抛出UnsupportedOperationException

我想安全的方法是将其声明为final并在构造函数中初始化它: http : //www.javamex.com/tutorials/synchronization_final.shtml

只要在读取开始之前初始化完成,就没有理由为什么HashMap的所有内容都不会对每个线程都可见。

这是正确的答案。

我打赌你的地图是静态字段,然后是的,没有同步就可以安全地读取它。

 class SomeClass static Map map = init(); 

这是因为JVM对类初始化执行了隐式的双重检查锁定。

基本上,你想要一个单身人士。 有几种技术,使用静态字段就是其中之一。 我敢打赌你的地图是一个“全局”的东西,所以很自然它是一个静态字段,因此线程安全。

有些情况下,懒惰的初始化数据结构不是全局的,因此我们需要其他单例实现方案,包括急切同步,易失性引用或通过中间最终引用。 请参阅维基百科双重检查锁定