java哈希映射线程可见性

时间:2011-03-02 14:44:23

标签: java multithreading concurrency

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

7 个答案:

答案 0 :(得分:8)

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

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

编辑:最初我完全忽略了地图如何初始化的问题。在阅读one of the Pugh articles之后(再次),似乎地图确实需要是最终的,以便初始化数据变得可见:

  

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

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

答案 1 :(得分:4)

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

  

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

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

答案 2 :(得分:2)

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

答案 3 :(得分:2)

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

map = Collections.unmodifiableMap(map);

如果某个线程调用了一个修改Map的操作,则会抛出UnsupportedOperationException

答案 4 :(得分:1)

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

答案 5 :(得分:0)

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

答案 6 :(得分:0)

这是正确答案。

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

class SomeClass
    static Map map = init();

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

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

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