如果映射初始化一次,并且永远不会再次修改,Hashmap 的 containsKey 方法是否线程安全



我们可以在不同步的情况下使用Hashmap的containsKey()方法吗?

注意:线程只会读取哈希图。地图初始化一次,并且永远不会再次修改。

这实际上取决于访问地图的方式/时间。
假设映射初始化一次,并且再也不修改,那么不修改内部状态的方法(如containsKey()应该是安全的。 但在这种情况下,您应该确保您的地图确实是不可变的,并且是安全发布的。

现在,如果在您的特定情况下,状态在您的程序过程中确实发生了变化,那么不,这是不安全的。 从文档中:

请注意,此实现不同步。 如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了映射,则必须在外部同步该映射。

在这种情况下,应使用ConcurrentHashMap或外部同步。

您不应该以这种方式看待单个方法。哈希图不适合在多线程设置中使用。

话虽如此,一个例外是:创建一次(单线程)的映射,之后仅"读取"。换句话说:如果地图不再被更改,那么您可以根据需要让任意数量的线程读取它。

从这个角度来看,仅仅containsKey()电话不应该引起问题。当此方法所依赖的信息随时间变化时,就会出现问题

不,它对任何操作都不是线程安全的。您需要同步所有访问或使用类似ConcurrentHashMap.

我最喜欢的生产系统故障排除恐怖故事是,我们发现HashMap.get由于缺少同步而进入无限循环,永远锁定了 100% CPU。发生这种情况是因为每个存储桶中使用的链表进入不一致状态。同样的情况也可能发生在containsKey.

如果在HashMap 最初发布后没有人修改它,你应该是安全的,但最好使用明确保证这一点的实现(例如 ImmutableMap 或 ConcurrentMap)。

No.(不,不是。一点也不。30个字符?

这很复杂,但大多数情况下,没有。

  1. HashMap的规范不作任何保证。因此,如果您尝试,它保留从扬声器中炸毁洋基涂鸦花花公子的权利:您只是不应该那样使用它。

  2. 。然而,在实践中,虽然HashMap的API不做任何保证,但通常它是有效的。但是,请注意@Thilo答案的恐怖故事。

  3. 。buuut,Java 内存模型的工作方式如下:您应该考虑每个线程都会获得整个 VM 堆中每个字段的单独副本。然后,这些单独的副本将在不确定的时间同步。这意味着各种代码根本无法正常工作;您从一个线程向地图添加一个条目,然后如果您从另一个线程访问该地图,即使经过很多时间,您也不会看到它——这在理论上是可能的。此外,在内部,map使用多个字段,大概这些字段必须彼此一致,否则你会得到奇怪的行为(异常和错误的结果)。JMM也不保证一致性。摆脱这种困境的方法是,JMM提供了这些称为"先来/来后"关系的东西,可以保证更改已同步。使用"syncd"关键字是建立这种关系的一种简单方法。

  4. 为什么不使用内置所有花里胡哨的ConcurrentHashMap,并且实际上可以保证从线程 A 添加一个条目,然后通过线程 B 的containsKey查询它会得到一个一致的答案(可能仍然是"不,该键不在映射中",因为也许线程 B 在线程 A 之前或稍晚到达那里,但你没有办法知道。它不会抛出任何异常或做一些非常奇怪的事情,例如突然为您很久以前添加的内容返回"false")。

所以,虽然它很复杂,但答案基本上是:不要那样做;要么使用同步防护,要么,可能是更好的选择:ConcurrentHashMap

不,请阅读 HashMap 文档的粗体部分:

请注意,此实现不同步。

所以你应该处理它:

如果多个线程同时访问哈希映射,并且至少有一个线程在结构上修改了映射,则必须在外部同步该映射。

并建议的解决方案:

这通常是通过在自然封装地图的某些对象上进行同步来实现的。如果不存在这样的对象,则应使用Collections.synchronizedMap方法"包装"映射

@user7294900是对的。

如果您的应用程序没有在结构上修改 HashMap,这是线程安全的构建,并且您的应用程序只是调用 containsKey 方法,那么它是线程安全的。

例如,我像这样使用HashMap:

@Component
public class SpringSingletonBean {
private Map<String, String> map = new HashMap<>();
public void doSomething() {
//
if (map.containsKey("aaaa")) {
//do something
}
}
@PostConstruct
public void init() {
// do something to initialize the map
}
}

效果很好。

最新更新