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

时间:2019-02-28 12:15:40

标签: java multithreading hashmap thread-safety

我们可以使用Hashmap的containsKey()方法而不在多线程环境中进行同步吗?

  

注意:线程只会读取Hashmap。该地图会被初始化一次,并且再也不会被修改。

7 个答案:

答案 0 :(得分:6)

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

现在,如果在您的特定情况下,状态在程序执行过程中确实发生了变化,那么否,那是不安全的。 来自documentation

  

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

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

答案 1 :(得分:3)

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

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

如果没有人在最初发布HashMap后对其进行修改,则应该是安全的,但是最好使用一种可以明确保证其实现的实现(例如ImmutableMap或ConcurrentMap)。

>

答案 2 :(得分:3)

您不应以这种方式查看单个方法。 HashMap 不是用于多线程设置中的。

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

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

答案 3 :(得分:2)

不。 (不,不是。完全没有。30个字符?)

答案 4 :(得分:2)

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

  

请注意,此实现未同步。

所以您应该处理它:

  

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

和建议的解决方案:

  

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

答案 5 :(得分:1)

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

  1. HashMap的规范不做任何保证。因此,如果您尝试这样做,它保留从扬声器中播放扬基涂鸦丹迪的权利:您不应该那样使用它。

  2. ...但是,实际上,虽然HashMap的API不做任何保证,但总的来说可以解决。但是,请记住@Thilo的回答的恐怖故事。

  3. ... buuut,Java内存模型的工作方式如下:您应该考虑到每个线程在VM的整个堆中都得到每个字段的单独副本。然后在不确定的时间同步这些单独的副本。这意味着各种代码根本无法正常工作。您从一个线程向地图添加了一个条目,然后从另一个线程访问该地图,即使经过了很多时间,您也不会看到它-从理论上讲这是可能的。同样,在内部,地图使用多个字段,并且推测这些字段必须彼此一致,否则您将获得怪异的行为(异常和错误的结果)。 JMM也不保证一致性。解决此难题的方法是,JMM提供了称为“先来/后来”的关系,这些关系使您可以确保更改已同步。使用'synchronized'关键字是保持这种关系的一种简单方法。

  4. 为什么不使用内置了所有功能的ConcurrentHashMap并实际上保证从线程A添加条目然后通过线程B的containsKey查询它为您提供一致的答案(可能仍然是“否,该键不在地图中”),因为线程B可能在线程A之前或之后不久才到达那里,但是您无法知道。它不会抛出任何异常异常或做一些真正奇怪的事情,例如突然为您以前添加的内容返回'false'。

因此,尽管很复杂,但答案基本上是:不要那样做;要么使用同步防护,要么使用更好的选择:ConcurrentHashMap

答案 6 :(得分:1)

@ 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
    }

}

效果很好。