Java 何时将concurrenthashmap与hashmap用于put和get
在一个采访问题中,我被要求解释一种情况,即使用concurrenthashmap与使用hashmap相比是正确的方法。在黑板上有两列t1和t2(对应于thread1和thread2),我应该编写一系列操作(比如Java 何时将concurrenthashmap与hashmap用于put和get,java,algorithm,concurrency,concurrenthashmap,Java,Algorithm,Concurrency,Concurrenthashmap,在一个采访问题中,我被要求解释一种情况,即使用concurrenthashmap与使用hashmap相比是正确的方法。在黑板上有两列t1和t2(对应于thread1和thread2),我应该编写一系列操作(比如map.put(2,10),map.get(2),等等),使用concurrenthashmap和hashmap会产生预期的结果 我试图用迭代器给出一个例子,但这不是面试官想要的。他正在寻找thread1和thread2的put和get操作序列。他说,假设我们从不迭代,我们只有put和ge
map.put(2,10)
,map.get(2)
,等等),使用concurrenthashmap和hashmap会产生预期的结果
我试图用迭代器给出一个例子,但这不是面试官想要的。他正在寻找thread1和thread2的put和get操作序列。他说,假设我们从不迭代,我们只有put和get操作
我查看了SO上的其他线程,验证了我对线程安全性的了解,但我仍然想不出任何put和Get使用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<>());
}
}
public class BreakingMap{
publicstaticvoidtestit(映射映射)抛出InterruptedException{
range(020000).parallel().forEach(i->map.put(i,-1));
System.out.println(map.size());
}
公共静态void main(字符串[]args)引发InterruptedException{
testIt(新HashMap());
testIt(新的ConcurrentHashMap());
}
}
这是一个有趣的问题
正确答案是:
在一些实际情况下,ConcurrentHashMap
上的一系列get和put操作将在多线程场景中产生预期结果。除了put()
,您几乎总是需要使用原子比较和变异操作,如computeifassent()
,来执行任何有用的操作。一个例外情况是,当您使用映射作为缓存时,让多个线程计算同一个条目的可能性比在一个线程执行时阻塞更有效。。。但是你真的需要缓存吗?不经常
为了记录在案,看起来是这样的:
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
,可能会导致未定义的行为—结果与任何操作序列不一致,空指针异常,甚至永久损坏数据结构
如果我在面试中问这个问题,我要测试的是:应聘者是否理解使用线程安全的数据结构不会使他的算法线程安全?如果使用不同线程的put/get,结果会不同,这就是ConcurrentMap/List/。。。,在顺序操作中,您不会有任何区别,因为Java规范中没有定义
HashMap
实现(只有API),所以当/ifHashMap
失败时,无法预测它。理论上,花边实现者可以复制并调整ConcurrentHashMap
。@MichaelButscher吹毛求疵,但唯一的声明是“注意这个实现没有同步。”@ErwinBolwidt对,所以问题是这是否意味着它在并发使用时可能失败或必须失败:)。出于实际原因,很明显“can fail”的意思是“can fail”。由于映射被破坏,您可以使用HashMap
。如果值是计数器(Integer
),则更容易看到故障模式。如果使用compute
来(原子地)更新它(显然没有AtomicInteger
那么优雅,但是演示了竞争),那么您可能会错过计数。