11

我的程序中有一个HashMap由多个线程访问,并且偶尔由单个线程设置。

例如:

Map<String, String> myMap = new HashMap<String, String>();

这是由多个线程访问的。每小时一次,单个线程调用:

myMap = myRefreshedVersionOfTheMap;

所以我的问题是这是否是线程安全的。如果两个地图总是有 key ,读取线程是否有可能在不存在"importantKey"的时候访问地图?"importantKey"

编辑:

感谢答案,我意识到这个问题实际上是独立于HashMap. 这更多是关于对象引用分配的问题。

4

4 回答 4

11

这不是线程安全的。即使在发布之后没有写入地图本身(从执行发布的线程的角度来看),并且引用分配是原子的,新的Map<>还没有安全发布。特别是,在 Map 的构造过程中会对其进行写入 - 在构造函数中或之后,取决于您添加这些元素的方式,并且这些写入可能会或可能不会被其他线程看到,因为即使它们直观地发生在map 被发布到其他线程,根据内存模型,这不是正式的情况。

为了安全地发布一个对象,它必须使用某种机制与外部世界通信,该机制要么在对象构造、引用发布和引用读取之间建立发生前的关系,要么必须使用一些更窄的方法,这些方法保证发布是安全的:

  • 从静态初始化程序初始化对象引用。
  • 将对它的引用存储到最终字段中。

如果您声明 myMap ,您的习语将是安全的volatile。有关安全发布的更多详细信息,请参阅 JCIP(强烈推荐)或此处,或有关类似主题的更长答案。

于 2013-06-20T17:52:28.967 回答
9

如果您的意思是您正在创建一个全新Map的并将其分配给myMap其他线程正在访问的内容,那么是的。引用分配是原子的。它是线程安全的,因为当其他线程正在从中读取内容时您没有修改内容 - 您只是有多个线程.MapMap

你只需要声明它,volatile这样其他线程就不会缓存它。

于 2013-06-20T17:31:16.023 回答
0

首先,Java 的 HashMap 类不是线程安全的,因此无法保证读取和写入何时同时发生。

但是,由于 Java 中对引用的读取和写入是原子的,因此只要刷新代码不会改变旧映射,您描述的模式就可以是线程安全的。例如,以下内容会很好:

// this refresh code would be thread-safe
Map<String, String> copy = new HashMap<String, String>(myMap);
copy.put(x, y); // change the map
myMap = copy;

// you might also consider
myMap = Collections.unmodifiableMap(copy);
// to make sure that the non-thread-safe map will never be mutated

这种模式需要考虑的一件事是,您可能希望将 myMap 字段声明为 volatile,以便所有线程在从该变量读取时都将获得最新版本的 myMap。

最后,正如其他海报所提到的,根据刷新代码的复杂性,ConcurrentHashMap 可能是更好的方法。ConcurrentHashMap 的一个缺点是它不提供任何批处理操作的方法,因此您必须确保刷新过程中每个点的状态对于应用程序的其余部分都有效。

于 2013-06-20T17:29:59.313 回答
-1

HashMap 不是线程安全的。您可以使用以下任何一种

  1. 并发哈希映射。
  2. HashMap 与外部同步。
  3. 每个线程都有不同的 HashMap。

在这里检查这个类似的答案

于 2013-06-20T17:32:50.570 回答