我的程序中有一个HashMap
,可由多个线程访问,偶尔由一个线程设置。
例如:
Map<String, String> myMap = new HashMap<String, String>();
多个线程可以访问它。每小时一次,一个线程调用:
myMap = myRefreshedVersionOfTheMap;
所以我的问题是这是否是线程安全的。如果两个地图始终都有密钥"importantKey"
,那么阅读线程是否有可能在"importantKey"
不存在时访问地图?
编辑:
感谢答案,我意识到这个问题实际上与HashMap
无关。这是一个关于对象引用分配的问题。
答案 0 :(得分:10)
这不是线程安全的。即使在发布点之后没有写入映射本身(从执行发布的线程的角度来看),并且引用赋值是原子的,新的Map<>
还没有安全发布。特别是, 在构造期间写入地图 - 无论是在构造函数中,还是之后,取决于您添加这些元素的方式,这些写入可能会或可能不会被其他线程看到,因为即使在将地图发布到其他线程之前直观地发生了,但根据内存模型,情况并非如此。
对于要安全发布的对象,必须使用某种机制将其传递给外部世界,该机制要么在对象构造,参考出版物和参考读取之间建立先发生的关系,要么必须使用少数保证发布安全的较窄方法:
如果你声明myMap volatile
,你的习语就会安全。有关安全发布的更多详细信息,请参见JCIP(强烈推荐)或here,或longer answer中有关类似主题的详细信息。
答案 1 :(得分:8)
如果您的意思是创建一个全新的Map
并将其分配给myMap
,这是其他线程正在访问的,那么是的。引用赋值是原子的。它是线程安全的,因为你没有修改Map
的内容而其他线程正在读取它 - 你只需要从Map
读取多个线程。
您只需要声明它volatile
,以便其他线程不会缓存它。
答案 2 :(得分: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的一个缺点是它没有提供批处理操作的任何方法,因此您必须确保刷新过程中每个点的状态对于应用程序的其余部分都有效。
答案 3 :(得分:-1)
HashMap不是线程安全的。您可以使用以下任何一种
检查此类似答案here