Java 设置HashMap线程安全吗?
我的程序中有一个Java 设置HashMap线程安全吗?,java,multithreading,reference,thread-safety,java-memory-model,Java,Multithreading,Reference,Thread Safety,Java Memory Model,我的程序中有一个HashMap,它由多个线程访问,偶尔由一个线程设置 例如: Map<String, String> myMap = new HashMap<String, String>(); 所以我的问题是这是否是线程安全的。如果两个映射始终都有键“importantKey”,那么当“importantKey”不存在时,读取线程是否可能访问映射 编辑: 多亏了这些答案,我意识到这个问题实际上独立于HashMap。这更像是一个关于对象引用分配的问题。首先,Java的H
HashMap
,它由多个线程访问,偶尔由一个线程设置
例如:
Map<String, String> myMap = new HashMap<String, String>();
所以我的问题是这是否是线程安全的。如果两个映射始终都有键“importantKey”
,那么当“importantKey”
不存在时,读取线程是否可能访问映射
编辑:
多亏了这些答案,我意识到这个问题实际上独立于
HashMap
。这更像是一个关于对象引用分配的问题。首先,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
//此刷新代码是线程安全的
映射副本=新HashMap(myMap);
复制。放置(x,y);//更改地图
myMap=复制;
你也可以考虑
myMap=集合。不可修改的映射(副本);
//确保非线程安全映射永远不会发生变异
这种模式考虑的一件事是,您可能希望MYMAP字段被声明为易失性,以便所有线程在从该变量读取时将获得最新版本的MYMAP。
最后,正如其他海报所提到的,ConcurrentHashMap可能是一种更好的方法,这取决于刷新代码的复杂性。ConcurrentHashMap的一个缺点是它不提供任何批处理操作的方法,因此您必须确保刷新过程中的每个点的状态对应用程序的其余部分都有效 如果您的意思是要创建一个全新的
Map
并将其分配给其他线程正在访问的myMap
,那么是的。引用赋值是原子的。它是线程安全的,因为当其他线程从映射中读取时,您不会修改映射的内容-您只是有多个线程从映射中读取
您只需要声明它volatile
,这样其他线程就不会缓存它了 HashMap不是线程安全的。您可以使用以下任何一项
ConcurrentHashMap
外部已同步的HashMap
每个线程有不同的HashMap
检查这个类似的答案这不是线程安全的。即使在发布点之后没有对映射本身的写入(从执行发布的线程的角度来看),并且引用分配是原子的,新的map
也没有安全地发布。特别是,在映射的构造过程中(在构造函数中或之后,取决于您添加这些元素的方式),会有对映射的写入,而这些写入可能会被其他线程看到,也可能不会被其他线程看到,因为即使它们直观地发生在映射发布到其他线程之前,根据内存模型,这种情况在形式上并非如此
对于要安全发布的对象,必须使用某种机制将其传达给外部世界,该机制要么在对象构造、引用发布和引用读取之间建立“先发生后发生”的关系,要么必须使用一些保证发布安全的较窄方法:
- 从静态初始值设定项初始化对象引用
- 将对它的引用存储到最终字段中
如果声明myMapvolatile
,您的习惯用法将是安全的。有关安全出版物的更多详细信息,请参见(强烈推荐),或,或类似主题的文章。这似乎不是他想要的;他没有并发读/写,只有读和引用赋值。这基本上就是我所做的。在刷新方法中,我完全构造了一个有效的替换映射,然后在单个操作中分配它。这是不正确的。即使没有读写操作,新的hashmap也不会安全地发布到外部世界。读取线程可以看到处于不确定状态的新HashMap映射。@BeeOnRope:虽然我可以看到对于任意数据结构都是这样,但我不相信java的HashMap是这样。该文档特别指出了在修改映射时需要进行同步:“如果多个线程同时访问一个哈希映射,并且至少有一个线程在结构上修改了该映射,则必须对其进行外部同步”(来自),该文档只是解释了哈希映射通常不是线程安全的(例如,与哈希表不同)安全发布规则是通用的,适用于包括HashMap在内的所有类。值得注意的是,在Java 5中引入内存模型之前就已经存在安全发布规则。我也不认为javadoc中的注释是ng是线程安全的必要条件,但不是充分条件。即使包含Map
的类是单例,您也需要volatile
;它是单例只意味着存在(保证)您的线程之间只共享该类的一个实例。这与多个线程访问任何其他类的单个实例没有什么不同。当您分配新的Map
时,其他线程不会看到它。这是不正确的。引用分配为“原子”只意味着线程看不到虚假引用(无处指向-即违反JVM的安全模型)。它不保证引用所指向的映射是完全构造的。另一个线程可以看到对新映射的引用,但随后访问仅部分构造的映射,其效果未知。volatile
是requ
// 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