根据 Java Concurrency in Practice,第 11.4.3 章说:
锁拆分有时可以扩展到对可变大小的独立对象集进行分区锁定,在这种情况下,它称为锁条带化。例如,ConcurrentHashMap 的实现使用了一个 16 个锁的数组,每个锁守卫 1/16 的哈希桶;桶 N 由锁 N mod 16 保护。
我在理解和可视化锁条带化和存储桶机制方面仍然存在问题。有人可以用很好理解的话来解释这个:)
提前致谢。
根据 Java Concurrency in Practice,第 11.4.3 章说:
锁拆分有时可以扩展到对可变大小的独立对象集进行分区锁定,在这种情况下,它称为锁条带化。例如,ConcurrentHashMap 的实现使用了一个 16 个锁的数组,每个锁守卫 1/16 的哈希桶;桶 N 由锁 N mod 16 保护。
我在理解和可视化锁条带化和存储桶机制方面仍然存在问题。有人可以用很好理解的话来解释这个:)
提前致谢。
哈希映射建立在一个数组上,其中哈希函数将一个对象映射到底层数组中的一个元素。假设底层数组有 1024 个元素 - ConcurrentHashMap 实际上把它变成了 16 个不同的 64 个元素的子数组,例如 {0, 63}, {64, 127} 等。每个子数组都有自己的锁,所以修改{0, 63} 子数组不影响 {64, 127} 子数组 - 一个线程可以写入第一个子数组,而另一个线程写入第二个子数组。
Collections.synchronizedMap()
锁在a和a的区别ConcurrentHashMap
如下:
如果多个线程将Collections.synchronizedMap()
频繁访问 a ,则会出现很多争用,因为每个方法都使用共享锁进行同步(即,如果线程 X 调用 a 上的方法Collections.synchronizedMap()
,则所有其他线程将被阻止调用 a 上的任何方法,Collections.synchronizedMap()
直到线程 X从它调用的方法返回)。
AConcurrentHashMap
具有可变数量的锁(默认为 16 个),每个锁保护ConcurrentHashMap
. 所以对于一个ConcurrentHashMap
有 160 把钥匙的钥匙,每把锁将保护 10 个元素。因此,对键(get
、put
、set
等...)进行操作的方法仅锁定对键在同一段中的键操作的其他方法的访问。例如,如果线程 X 调用put(0, someObject)
,然后线程 Y 调用put(10, someOtherObject)
这些调用可以并发执行,并且线程 Y 不必等待线程 X 从返回put(0, someObject)
。下面提供了一个示例。
此外,某些方法(例如size()
和isEmpty()
)根本不受保护。虽然这允许更大的并发性,但这意味着它们不是强一致的(它们不会反映同时发生变化的状态)。
public static void main(String[] args) {
ConcurrentHashMap<Integer, Object> map = new ConcurrentHashMap<>(160);
new Thread(new Runnable() {
@Override
public void run() {
map.put(0, "guarded by one lock");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
map.put(10, "guarded by another lock");
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
// could print 0, 1, or 2
System.out.println(map.count());
}
}.start();
}
这里的关键概念是“桶”。而是为整个哈希表使用全局锁,它为每个存储桶使用一个小锁。这也是一个很好的类比桶排序,可以提高排序的复杂性。