只读取它时是否必须使用线程安全的Map实现?

时间:2011-05-10 13:54:10

标签: java multithreading concurrency

如果我执行以下操作。

  • 创建HashMap(在最终字段中)
  • 填充HashMap
  • 使用不可修改的包装器Map
  • 包装HashMap
  • 启动将访问但不修改Map
  • 的其他线程

据我所知,Map已经“安全发布”,因为其他线程是在Map完全填充后启动的,所以我认为可以从多个线程访问Map,因为在此之后无法修改它。 / p>

这是对的吗?

5 个答案:

答案 0 :(得分:11)

关于地图本身,这完全没问题。但是你需要意识到使地图不可修改只会使地图本身不可修改并且其键和值。因此,如果你有一个Map<String, SomeMutableObject>,例如Map<String, List<String>>,那么线程仍然可以通过例如map.get("foo").add("bar");改变该值。为避免这种情况,您还希望使键/值不可变/不可修改。

答案 1 :(得分:1)

  

据我所知,Map已经“安全发布”,因为其他线程是在Map完全填充后启动的,所以我认为可以从多个线程访问Map,因为在此之后无法修改点。

是。只需确保其他线程以同步方式启动,即确保在发布映射和启动线程之间有happens-before relation

这在this blog post中讨论:

  

[...] 这就是Collections.unmodifiableMap()的工作原理。

     

[...]

     

由于关键字“final”的特殊含义,此类的实例可以与多个线程共享,而无需使用任何其他同步;当另一个线程在实例上调用get()时,可以保证获取放入映射的对象,而不进行任何其他同步。您应该使用线程安全的东西来执行线程之间的切换(比如LinkedBlockingQueue或其他东西),但如果您忘记这样做,那么您仍然可以获得保证。

答案 2 :(得分:0)

你是对的。不需要通过使用互斥锁或其他方式确保不同线程对数据结构的独占访问,因为它是不可变的。这通常会大大提高性能。

另请注意,如果您只包装原始Map而不是创建副本,即不可修改的Map委托方法进一步调用内部HashMap,修改底层Map可能会引入竞争条件问题。

答案 3 :(得分:0)

不可变的地图是线程安全的。你可以使用Guava的ImmutableMap。

答案 4 :(得分:0)

简而言之,如果读取是非破坏性的并且地图引用安全地发布到客户端,则不需要映射是线程安全的。

在示例中,此处建立了两个重要的 happens-before 关系。最终字段发布(当且仅当在构造函数内完成填充并且引用不会在构造函数外部泄漏时)以及调用启动线程。

在客户从地图上读取这些调用之后修改地图的任何内容都无法安全发布。

我们有一个CopyOnWriteMap,它具有一个非线程安全的映射,在每次写入时都会被复制。在读取次数多于写入的情况下,这种情况尽可能快(缓存配置数据就是一个很好的例子)。

也就是说,如果目的是为了不改变地图,那么将地图的不可变版本设置到字段中总是最好的方法,因为它可以保证客户端看到正确的内容。

最后,有一些Map实施具有破坏性读取,例如具有访问顺序的LinkedHashMap或条目可能消失的WeakHashMap。必须连续访问这些类型的地图