ConcurrentHashMap 的 compute、computeIfAbsent 和 computeIfPresent 方法是完全原子的吗?
Are ConcurrentHashMap's compute, computeIfAbsent and computeIfPresent methods entirely atomic?
整个方法调用是原子的还是只是 BiFunction 执行是原子的?它是阻塞所有键还是只阻塞同一键上的调用?
以下详细信息适用于 OpenJDK Java11.
这三个方法在调用方法和更新键/值时锁定映射中的Node
。该节点通常是哈希桶的链或节点树中的第一个节点。并发尝试在同一存储桶中插入、更新或删除 key/value 对将被阻止,直到释放锁。
(其他版本 Java 的行为可能不同。)
Is the whole method call atomic or is just the BiFunction execution atomic?
整个方法调用。
Is it blocking for all keys or just for the calls on the same key?
介于两者之间;看上面。但是,如果您遵循 javadocs 中的此建议,应该没有关系。
" Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple ..."
我刚刚在 Android 上使用以下代码
在 Kotlin 协程上对此进行了测试
val incrementalNum = AtomicInteger(0)
val map = ConcurrentHashMap<Int, Unit>()
val lastThreadNum = AtomicInteger(-1)
val collisions = AtomicInteger(0)
for (a in 0..1000) GlobalScope.launch {
val threadNum = incrementalNum.incrementAndGet()
map.compute(threadNum) { k, v-> lastThreadNum.set(threadNum); Unit.apply {
Log.e("SLOW", "this operation will slow things")
if (lastThreadNum.get() != threadNum) Log.e("COLLISION", "Collision Number: ${collisions.incrementAndGet()}")
}}
}
使用 map.compute(threadNum) 我每次 运行 都会遇到相当多的碰撞(大约 10-30 次),而使用 map.compute(1) 我从来没有得到任何碰撞。
这似乎表明 - 至少在 Android - ConcurrentHashMap.compute 函数只是按键阻塞(这对我的用例来说是一个很好的行为)
我会尝试用其他 Java 实现来测试它,如果那是 standard/usual 实现,可能应该更好地记录它
整个方法调用是原子的还是只是 BiFunction 执行是原子的?它是阻塞所有键还是只阻塞同一键上的调用?
以下详细信息适用于 OpenJDK Java11.
这三个方法在调用方法和更新键/值时锁定映射中的Node
。该节点通常是哈希桶的链或节点树中的第一个节点。并发尝试在同一存储桶中插入、更新或删除 key/value 对将被阻止,直到释放锁。
(其他版本 Java 的行为可能不同。)
Is the whole method call atomic or is just the BiFunction execution atomic?
整个方法调用。
Is it blocking for all keys or just for the calls on the same key?
介于两者之间;看上面。但是,如果您遵循 javadocs 中的此建议,应该没有关系。
" Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple ..."
我刚刚在 Android 上使用以下代码
在 Kotlin 协程上对此进行了测试val incrementalNum = AtomicInteger(0)
val map = ConcurrentHashMap<Int, Unit>()
val lastThreadNum = AtomicInteger(-1)
val collisions = AtomicInteger(0)
for (a in 0..1000) GlobalScope.launch {
val threadNum = incrementalNum.incrementAndGet()
map.compute(threadNum) { k, v-> lastThreadNum.set(threadNum); Unit.apply {
Log.e("SLOW", "this operation will slow things")
if (lastThreadNum.get() != threadNum) Log.e("COLLISION", "Collision Number: ${collisions.incrementAndGet()}")
}}
}
使用 map.compute(threadNum) 我每次 运行 都会遇到相当多的碰撞(大约 10-30 次),而使用 map.compute(1) 我从来没有得到任何碰撞。
这似乎表明 - 至少在 Android - ConcurrentHashMap.compute 函数只是按键阻塞(这对我的用例来说是一个很好的行为)
我会尝试用其他 Java 实现来测试它,如果那是 standard/usual 实现,可能应该更好地记录它