设置HashMap线程安全吗?

Cor*_*han 10 java multithreading reference thread-safety java-memory-model

HashMap我的程序中有一个由多个线程访问,偶尔由一个线程设置.

例如:

Map<String, String> myMap = new HashMap<String, String>();
Run Code Online (Sandbox Code Playgroud)

这可以通过多个线程访问.每小时一次,一个线程调用:

myMap = myRefreshedVersionOfTheMap;
Run Code Online (Sandbox Code Playgroud)

所以我的问题是这是否是线程安全的.如果两个映射始终都有密钥"importantKey",那么读取线程是否可以在"importantKey"不存在时访问映射?

编辑:

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

Bee*_*ope 10

这不是线程安全的.即使在发布点之后没有对映射本身进行写入(从执行发布的线程的角度来看),并且引用赋值是原子的,但新的Map<>尚未安全发布.它特别有它的建设过程中写入到地图-无论是在构造函数中,或之后,这取决于你如何添加这些元素,这些写入可能会或可能不会被其他线程看到的,因为即使他们直观地前出现map是发布到其他线程的,这不是根据内存模型的正式情况.

对于要安全发布的对象,必须使用某种机制将其传达给外部世界,该机制要么在对象构造,参考出版物和参考读​​取之间建立先发生的关系,要么必须使用一些较窄的方法,保证发布安全:

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

如果你声明了myMap,那么你的习语是安全的volatile.关于安全发布的更多细节可以在JCIP(强烈推荐)中找到,或者在这里,或者在类似主题的更长答案中找到.

  • @CorayThan - 不完全是.你甚至不需要将值设置为null,并且你不需要使用`myRefreshedVersionOfMap`所有这些东西(添加它没有区别).我是说如果你在构造时用一个键值"foo = bar"初始化`myMap`,然后是另一个线程设置`myMap = otherMap`,其中otherMap被创建为`otherMap = new HashMap(); otherMap.put("foo","dog")`,你期望每个线程都能看到foo = bar或foo = dog的地图.实际上,你可以看到一张完全没有键的地图,一张带有空键的地图,一张带有foo = null的地图. (3认同)

Bri*_*ach 8

如果你的意思是你正在创建一个全新的Map并将其分配给myMap其他线程正在访问的内容,那么是的.引用赋值是原子的.这是线程安全的,因为你没有修改内容Map,而其他线程正在从中读取-你只需要多线程从阅读Map.

你只需要声明它,volatile以便其他线程不缓存它.

  • 这是不正确的.引用赋值为"原子"只意味着线程无法看到伪造的引用(无处指向 - 即违反JVM的安全模型).它不保证引用指向的Map <>是完全构造的.另一个线程可以看到对新地图的引用,但随后访问Map <>,它只是部分构造,具有未知效果.这里需要`volatile`来安全发布,而不仅仅是为了避免缓存. (3认同)
  • 即使包含`Map`的类是单例,你也需要`volatile`; 它是一个单例只意味着(保证)你的线程中只共享该类的一个实例.这与访问任何其他类的单个实例的多个线程没有什么不同.当你指定新的`Map`时,其他线程也不会看到它. (2认同)
  • 您偶然提到了volatile以避免其他线程缓存它 - 换句话说它仍然是安全的,但是由于缓存,您可能会看到过时的值.这是不正确的 - 无论陈旧值如何,它都是完全不安全的.您的程序可以终止,表现异常,进入无限循环等. (2认同)