ConcurrentHashMap:每个键仅调用一种方法
每种方法ConcurrentHashMap
都是线程安全的。但是,从ConcurrentHashMap
同一键调用多个方法会导致争用条件。从ConcurrentHashMap
不同的键递归调用相同的方法会导致死锁。
让我们看一个例子,看看为什么会发生这种情况:
调用多个方法
在下面的测试中,我ConcurrentHashMap
对相同的键1 使用了from的两个方法。第update
3行到第10行的方法 首先从ConcurrentHashMap
using方法 获取值 get
。比update增加值,并使用方法put
第6行和第8行将其放回 :
为了测试会发生什么,我使用在第18和21行中创建的两个线程。我在第25和25行中启动了这两个线程。然后等待,直到在26和27行中使用线程连接都结束了。在两个线程都停止之后我检查第28行的值是否确实为2。
为了测试所有线程交织,我们将完整的测试放在while循环中,使用AllInterleavings
来自vmlens的类(第15行)遍历所有线程交织 。运行测试,我看到以下错误:
要查看为什么结果是一个而不是预期的两个,我们可以查看生成的报告vmlens:
所以。问题是首先两个线程都调用get,
,然后两个线程都调用put
。因此,两个线程都看到一个空值,并将该值更新为1。这导致结果为一,而不是预期的二。解决此竞争条件的技巧是仅使用一种方法而不是两种方法来更新值。使用方法compute
,我们可以做到这一点。因此,正确的版本如下所示:
递归调用相同的方法
现在,让我们来看一个从ConcurrentHashMap
递归调用相同方法的示例:
在这里,我们为不同的键调用compute
方法内部的compute
方法。一次输入**,然后输入两个,一次输入**,然后输入两个。如果运行测试,则会看到以下死锁:
要了解为什么会发生这种僵局,我们必须查看的内部ConcurrentHashMap
。ConcurrentHashMap
使用数组存储键和值之间的映射。每次我们更新的映射时,ConcurrentHashMap,
它都会锁定存储该映射的数组元素。因此,在我们的测试中,对**一进行计算的调用锁定了**一的数组元素。然后,我们尝试锁定键2的数组元素。但是此**已经被另一个线程锁定,该线程为**2调用compute,并尝试锁定**1的数组元素。僵局。
请注意,只有更新才需要锁定数组元素。只读方法(例如)get
不使用锁。因此,get
在compute
调用内使用方法是没有问题的。
结论
使用ConcurrentHashMap
一个线程安全的方式是很容易。选择一种最适合你需要的方法,然后每个键只使用一次
有什么问题可以加下qq:2062583349。也可添加vx:admindesire,有java、python、web等习资料和视频课程干货”。欢迎交流!