我想将预先构建的 Map 传递给多个线程,这些线程只会同时从 Map 中读取值。在这种情况下,我是否需要担心并发访问并使用 ConcurrentHashMap 而不是 HashMap?
7 回答
您仍然需要确保在创建和初始化 Map 的线程与随后读取它的线程之间存在“happens-before”关系。如果没有这样的关系,理论上读线程有可能看到陈旧的数据。
如果您在启动其他线程的线程中创建并填充 Map ......在它执行此操作之前......你应该没问题。
确保安全的另一种简单方法是执行以下操作:
public class Foo {
private final Map map;
public Foo(....) {
map = new HashMap();
// populate
}
public Map getMap() {
// If the 'map' is final, and nothing changes it apart from
// the constructor, this method doesn't need to be synchronized.
}
这两者都会导致创建/初始化和每个线程第一次使用 Map 之间的“先发生”关系。
您也可以map
在上面声明为volatile
,但可能会导致比严格需要的更多的缓存刷新;即你会受到小的性能影响。
此外,为了确保仅针对读取操作进行处理,您可以做的是返回Collections.unmodifiableMap(Map)。
理论上,并发读取不应该对集合造成问题。
但是如果你修改一次,很容易出错,那么你应该使用并发版本。
如果所有线程都在读取它,则传递 HashMap 是安全的。但是,在传递地图之前,我会调用 Collections.unmodifiableMap() 以确保如果其中一个线程意外修改它,因为将来某些程序员不尊重当前的合同,你会得到一个例外。
你总是需要担心线程安全。在您的具体示例中,如果您
- 在单个线程中完全构建地图;
volatile
通过设置一个变量来引用它,将它发布到其他线程。
或者,如果这是一个选项,请确保在映射准备好之前不启动线程。
不,您不必担心。
除非有人在线程访问它时更改预建地图。
不,你不需要使用它。仅在以下情况下会产生冲突:
- 当一些线程可能会读取数据,而其他线程可能会尝试覆盖它。
- 第二个是当每个线程都尝试更新数据时。
当每个线程仅尝试读取值时,不会产生冲突。