[英]When to use concurrenthashmap vs hashmap for put and get
在一个面试问题中,我被要求解释一种情况,即使用并发哈希图与使用哈希图相比是正确的方法。 在板上有两列 t1 和 t2(对应于线程 1 和线程 2),我应该编写一系列操作(如map.put(2, 10)
、 map.get(2)
等),使用concurrenthashmap 与 hashmap 会产生预期的结果。
我试图用迭代器举一个例子,但这不是面试官想要的。 他正在寻找线程 1 和线程 2 的放置和获取操作序列。 他说假设我们从不迭代,我们只有 put 和 get 操作。
我查看了 SO 上的其他线程并验证了我对线程安全的了解,但我仍然想不出任何关于 put 的示例,并使用 hashmap 生成错误的结果,并使用 concurrenthashmap 生成正确的结果。 是否有任何放置和获取的顺序,或者我应该说不可能?
它们有很多不同的方式 - 因为 HashMap 没有保护免受来自多个线程的并发访问,您可以完全破坏其内部数据结构。
但是,您必须经常获得更良性的效果。 下面的代码应该从多个线程在每个映射中放置 2000 个条目。 但是对于 HashMap,操作后映射中的条目将始终少于 2000,因为一些 put 会相互冲突,并且它们的结果将丢失。
public class BreakingMap {
public static void testIt(Map<Integer, Integer> map) throws InterruptedException {
IntStream.range(0, 2000).parallel().forEach(i -> map.put(i, -1));
System.out.println(map.size());
}
public static void main(String[] args) throws InterruptedException {
testIt(new HashMap<>());
testIt(new ConcurrentHashMap<>());
}
}
这是一个有趣的问题。
正确答案是:
在多线程场景中, ConcurrentHashMap
上的一系列 get 和 put 操作将产生预期结果的现实情况很少。 代替put()
,您几乎总是需要使用像computeIfAbsent()
这样的原子比较和变异操作来做任何有用的事情。 一种例外情况是,当您将地图用作缓存并且让多个线程计算同一个条目的可能性比一个线程执行时阻塞的效率更高……但是您真的需要缓存吗? 不经常。
只是为了记录,这看起来像这样:
Thread1 + Thread2 (they both do the same thing)
-----------------------------------------------
result = map.get(key);
if (result == null) {
result = somewhat_expensive_function(key)
map.put(key, result);
}
return result;
另一方面,当一个线程可能正在修改映射而另一个线程也在使用它时,跨两个线程使用普通HashMap
会导致未定义的行为——结果与任何操作序列不一致,空指针异常,甚至永久损坏的数据结构。
如果我在面试中问这个问题,我要测试的是:应聘者是否理解使用线程安全的数据结构不会使他的算法成为线程安全的?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.