在一个采访问题中,我被要求解释一种情况,即使用并发哈希图与使用哈希图是正确的方法。板上有两列t1和t2(分别对应于线程1和线程2),我应该编写一系列操作(例如map.put(2, 10)
,map.get(2)
等),这些操作使用并发哈希图与哈希图会产生预期的结果。
我试图举一个带有迭代器的例子,但这不是访问者想要的。他正在寻找线程1和线程2的放置和获取操作序列。他说,假设我们从不进行迭代,而仅进行操作即可。
我查看了SO上的其他线程并验证了我对线程安全性的了解,但是我仍然想不出任何放置示例,并使用hashmap产生错误的结果,并使用并发hashmap产生正确的结果。是否有任何放入和取出的顺序,或者我应该说不可能?
答案 0 :(得分:1)
它们有很多区别的方式-由于HashMap不能防止来自多个线程的并发访问,因此您可以完全破坏其内部数据结构。
但是您经常必须获得更好的效果。下面的代码应该在多个线程的每个映射中放置2000个条目。但是对于HashMap而言,操作后映射中的条目将始终少于2000,因为某些puts会相互冲突,其结果将丢失。
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<>());
}
}
答案 1 :(得分:0)
这是一个有趣的问题。
正确的答案是:
在少数现实情况下,在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
可能导致未定义的行为-结果与任何操作序列均不一致,null指针异常,甚至是永久损坏的数据结构。
如果我在面试中问这个问题,我将要测试的是:应试者是否理解使用线程安全的数据结构不会使他的算法具有线程安全性? >