分段哈希映射。更多细分会增加还是降低效果?

时间:2017-12-19 21:48:58

标签: java concurrency hashmap

我正在尝试了解分段哈希映射的工作原理。我知道他们使哈希映射更安全,但是我不清楚分割数据是否会增加每秒可以在给定哈希映射上执行的操作的总数。

例如,如果我有一个包含10,000个元素的哈希映射并且逐渐增加了段数,那么我是否会看到程序吞吐量和每秒操作数的增加或减少?

1 个答案:

答案 0 :(得分:0)

concurrencyLevel参数在现代实现中没有任何作用,只存在以保持API与早期版本的JDK的兼容性,或者像Javadoc所说的那样:

  

此外,为了与此类的先前版本兼容,构造函数可以选择指定预期的concurrencyLevel作为内部大小调整的附加提示。

implemented如下:

public ConcurrentHashMap(int initialCapacity,
                         float loadFactor, int concurrencyLevel) {
    if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
        throw new IllegalArgumentException();
    if (initialCapacity < concurrencyLevel)   // Use at least as many bins
        initialCapacity = concurrencyLevel;   // as estimated threads
    long size = (long)(1.0 + (long)initialCapacity / loadFactor);
    int cap = (size >= (long)MAXIMUM_CAPACITY) ?
        MAXIMUM_CAPACITY : tableSizeFor((int)size);
    this.sizeCtl = cap;
}

也就是说,如果用户指定了不切实际的低concurrencyLevel,则initialCapacity仅覆盖initialCapacity。这就是全部。

此外,这仅仅设定了地图的初始容量;实际容量将随着条目数量的增加而增加(按照loadFactor的指示)。

总之,concurrencyLevel除了最模糊的用例之外都没有持久的影响(一个具有错误指定的初始容量的映射包含比访问它的线程更少的条目,并且由于所有线程花费大部分时间而受到严重争议他们与该地图互动的时间)。

您还问:

  

由于每个细分市场本质上都是一个锁定,因此肯定会有更多的网段减慢程序的速度,因为这意味着某些方法和操作必须等待?

要获取或放置值,线程将仅锁定包含它的段。也就是说,将映射划分为更多段不会导致在每个操作中获取或释放更多锁,但会降低锁争用的可能性,因为每个段的条目较少。

另外,值得注意的是ConcurrentHashMap中的许多方法都是等待的。例如,以下是获取值的方法:

public V get(Object key) {
    Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
    int h = spread(key.hashCode());
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (e = tabAt(tab, (n - 1) & h)) != null) {
        if ((eh = e.hash) == h) {
            if ((ek = e.key) == key || (ek != null && key.equals(ek)))
                return e.val;
        }
        else if (eh < 0)
            return (p = e.find(h, key)) != null ? p.val : null;
        while ((e = e.next) != null) {
            if (e.hash == h &&
                ((ek = e.key) == key || (ek != null && key.equals(ek))))
                return e.val;
        }
    }
    return null;
}