需要简单解释“锁定条带化”如何与ConcurrentHashMap一起使用

根据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()ConcurrentHashMap之间的区别如下:

如果多个线程经常访问Collections.synchronizedMap() ,则会有很多争用,因为每个方法都使用共享锁同步(即如果线程X调用Collections.synchronizedMap()上的方法,则所有其他线程将是阻止调用Collections.synchronizedMap()上的任何方法,直到线程X从它调用的方法返回为止。

ConcurrentHashMap具有可变数量的锁(默认值为16),每个锁都保护ConcurrentHashMap的一段键。 因此对于具有160个键的ConcurrentHashMap ,每个锁将保护10个元素。 因此,对键( getputset等)进行操作的方法仅锁定对在键处于同一段中的键上操作的其他方法的访问。 例如,如果线程X调用put(0, someObject) ,然后线程Y调用put(10, someOtherObject)那些调用可以并发执行,并且线程Y不必等待线程X从put(0, someObject)返回put(0, someObject) 。 下面提供一个例子。

此外,某些方法(如size()isEmpty()根本没有保护。 虽然这允许更大的并发性,但这意味着它们不是强一致的(它们不会反映同时发生变化的状态)。

 public static void main(String[] args) { ConcurrentHashMap 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(); } 

这里的关键概念是“桶”。 相反,对整个哈希表使用全局锁,它为每个桶使用一个小锁。 它也类似于桶式排序,可以提高排序复杂性。