java 需要简单解释“锁条带化”如何与 ConcurrentHashMap 配合使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16151606/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 22:03:32  来源:igfitidea点击:

Need simple explanation how "lock striping" works with ConcurrentHashMap

javaconcurrencyjava.util.concurrentconcurrenthashmap

提问by GedankenNebel

According to Java Concurrency in Practice, chapter 11.4.3 says:

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

Lock splitting can sometimes be extended to partition locking on a variablesized set of independent objects, in which case it is called lock striping. For example, the implementation of ConcurrentHashMap uses an array of 16 locks, each of which guards 1/16 of the hash buckets; bucket N is guarded by lock N mod 16.

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

I still have problems to understand and visualize the lock striping and buckets mechanism. Can someone explain this with good understanding words :)

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

Thanks in advance.

提前致谢。

回答by Zim-Zam O'Pootertoot

The hash map is built on an array, where the hash function maps an object to an element in the underlying array. Let's say the underlying array has 1024 elements - ConcurrentHashMap actually turns this into 16 different sub-arrays of 64 elements, e.g. {0, 63}, {64, 127}, etc. Each sub-array has its own lock, so modifying the {0, 63} sub-array doesn't impact the {64, 127} sub-array - one thread can write to the first sub-array while another thread writes to the second sub-array.

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

回答by ashish_388235

The difference between locking in a Collections.synchronizedMap()and a ConcurrentHashMapis as follows:

在aCollections.synchronizedMap()和a中加锁的区别ConcurrentHashMap如下:

If multiple threads will access a Collections.synchronizedMap()frequently, there will be a lot of contention since each method is synchronized using a shared lock (i.e. if thread X calls a method on a Collections.synchronizedMap(), all other threads will be blocked from calling any method on a Collections.synchronizedMap()until thread X returns from the method it called).

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

A ConcurrentHashMaphas a variable number of locks (default is 16) that each guard a segment of the keys in the ConcurrentHashMap. So for a ConcurrentHashMapwith 160 keys, each lock will guard 10 elements. Therefore, methods operating on a key (get, put, set, etc...) only lock out access to other methods operating on a key where the keys are in the same segment. For example, if thread X calls put(0, someObject), and then thread Y calls put(10, someOtherObject)those calls can execute concurrently, and thread Y does not have to wait for thread X to return from put(0, someObject). An example is provided below.

AConcurrentHashMap具有可变数量的锁(默认为 16),每个锁保护ConcurrentHashMap. 所以对于一个ConcurrentHashMap有 160 把钥匙的钥匙,每把锁将保护 10 个元素。因此,对键(getputset等...)进行操作的方法只会锁定对键位于同一段中的键进行操作的其他方法的访问。例如,如果线程 X 调用put(0, someObject),然后线程 Y 调用put(10, someOtherObject)这些调用可以并发执行,并且线程 Y 不必等待线程 X 从 返回put(0, someObject)。下面提供了一个示例。

Additionally, certain methods such as size()and isEmpty()are not guarded at all. While this allows for greater concurrency, it means they are not strongly-consistent (they won't reflect state that is concurrently changing).

此外,某些方法(例如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();
}

回答by BufBills

The key concept here is the "bucket" . instead using a global lock for this whole hash Table, it uses one small lock for each bucket. It's also a good analogous to bucket sort which can improve sorting complexity.

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