27

根据 Java Concurrency in Practice,第 11.4.3 章说:

锁拆分有时可以扩展到对可变大小的独立对象集进行分区锁定,在这种情况下,它称为锁条带化。例如,ConcurrentHashMap 的实现使用了一个 16 个锁的数组,每个锁守卫 1/16 的哈希桶;桶 N 由锁 N mod 16 保护。

我在理解和可视化锁条带化和存储桶机制方面仍然存在问题。有人可以用很好理解的话来解释这个:)

提前致谢。

4

3 回答 3

52

哈希映射建立在一个数组上,其中哈希函数将一个对象映射到底层数组中的一个元素。假设底层数组有 1024 个元素 - ConcurrentHashMap 实际上把它变成了 16 个不同的 64 个元素的子数组,例如 {0, 63}, {64, 127} 等。每个子数组都有自己的锁,所以修改{0, 63} 子数组不影响 {64, 127} 子数组 - 一个线程可以写入第一个子数组,而另一个线程写入第二个子数组。

于 2013-04-22T16:12:37.500 回答
16

Collections.synchronizedMap()锁在a和a的区别ConcurrentHashMap如下:

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

AConcurrentHashMap具有可变数量的锁(默认为 16 个),每个锁保护ConcurrentHashMap. 所以对于一个ConcurrentHashMap有 160 把钥匙的钥匙,每把锁将保护 10 个元素。因此,对键(getputset等...)进行操作的方法仅锁定对键在同一段中的键操作的其他方法的访问。例如,如果线程 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();
}
于 2013-04-22T16:22:58.387 回答
3

这里的关键概念是“桶”。而是为整个哈希表使用全局锁,它为每个存储桶使用一个小锁。这也是一个很好的类比桶排序,可以提高排序的复杂性。

于 2015-06-17T18:22:56.603 回答