我们可以使用Hashmap的containsKey()
方法而不在多线程环境中进行同步吗?
注意:线程只会读取Hashmap。该地图会被初始化一次,并且再也不会被修改。
答案 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)
这很复杂,但大多数情况下没有。
HashMap的规范不做任何保证。因此,如果您尝试这样做,它保留从扬声器中播放扬基涂鸦丹迪的权利:您不应该那样使用它。
...但是,实际上,虽然HashMap的API不做任何保证,但总的来说可以解决。但是,请记住@Thilo的回答的恐怖故事。
... buuut,Java内存模型的工作方式如下:您应该考虑到每个线程在VM的整个堆中都得到每个字段的单独副本。然后在不确定的时间同步这些单独的副本。这意味着各种代码根本无法正常工作。您从一个线程向地图添加了一个条目,然后从另一个线程访问该地图,即使经过了很多时间,您也不会看到它-从理论上讲这是可能的。同样,在内部,地图使用多个字段,并且推测这些字段必须彼此一致,否则您将获得怪异的行为(异常和错误的结果)。 JMM也不保证一致性。解决此难题的方法是,JMM提供了称为“先来/后来”的关系,这些关系使您可以确保更改已同步。使用'synchronized'关键字是保持这种关系的一种简单方法。
为什么不使用内置了所有功能的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
}
}
效果很好。