简体   繁体   English

Java 8+ ConcurrentHashMap锁定条带化

[英]Java 8+ ConcurrentHashMap lock striping

I have been reading through Concurency in Practice by Brian Goetz. 我一直在读Brian Goetz的Concurency in Practice

In the chapter about Lock Striping it is written that ConcurrentHashMap uses 16 buckets to improve multithreaded access by many threads : 在关于锁定条带的章节中,写入ConcurrentHashMap使用16个桶来改进许多线程的多线程访问:

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; 例如,ConcurrentHashMap的实现使用一个包含16个锁的数组,每个锁保护1/16的散列桶; bucket N is guarded by lock N mod 16. 铲斗N由锁定N mod 16保护。

I have read those questions : 我读过这些问题:

ConcurrentHashMap locking ConcurrentHashMap锁定

Need simple explanation how “lock striping” works with ConcurrentHashMap 需要简单解释“锁定条带化”如何与ConcurrentHashMap一起使用

However those answers are valid for Java version <= 7. 但是,这些答案对Java版本<= 7有效。

For Java 8+ the behaviour seems to be changed significantly. 对于Java 8+,行为似乎发生了显着变化。 For Java 8+ it seems that that the lock is acquired not for a Segment but for particular Node in table ( transient volatile ConcurrentHashMap.Node<K, V>[] table; ). 对于Java 8+,似乎锁定不是针对Segment而是针对表中的特定节点而获取的( transient volatile ConcurrentHashMap.Node<K, V>[] table; )。 For example for the putVal operation : 例如对于putVal操作:

ConcurrentHashMap.Node var7;

.... ///retrive node for var7

synchronized(var7) {
....
}

And also from Java8 + field like DEFAULT_CONCURRENCY_LEVEL and class Segment seems to be unused in the implementation (it is only used in private method writeObject::ObjectOutputStream and this method is not invoked anywhere in ConcurrentHashMap implementation). 而且从Java8 +字段开始,如DEFAULT_CONCURRENCY_LEVEL和类Segment似乎在实现中未使用(它仅在私有方法writeObject::ObjectOutputStream并且此方法不会在ConcurrentHashMap实现中的任何位置调用)。

  1. What is the cause of such significant change in ConcurrentHashMap implementation? ConcurrentHashMap实现中这种重大变化的原因是什么?

  2. If class Segment is unused and also field like DEFAULT_CONCURRENCY_LEVEL is also unused - why not get rid of it from implementation - is it for some historical reasons? 如果类Segment未使用,并且DEFAULT_CONCURRENCY_LEVEL类的字段也未使用 - 为什么不从实现中删除它 - 是否出于某些历史原因?

  3. If we are not locking on segments, like it used to be for Java version < 7, is locking only on specific node sufficient? 如果我们没有锁定段,就像以前的Java版本<7那样,只能在特定节点上锁定吗? If yes - why? 如果是 - 为什么? Does that mean that we do not need lock striping here? 这是否意味着我们不需要锁定条带?

What is the cause of such significant change in ConcurrentHashMap implementation? ConcurrentHashMap实现中这种重大变化的原因是什么?

To reduce memory footprint (one of the reasons) . 减少内存占用(原因之一)

We do not want to waste the space required to associate a distinct lock object with each bin, so instead use the first node of a bin list itself as a lock. 我们不想浪费将不同锁对象与每个bin关联所需的空间,因此使用bin列表本身的第一个节点作为锁。 Locking support for these locks relies on builtin "synchronized" monitors. 锁定对这些锁的支持依赖于内置的“同步”监视器。

openjdk > jdk8 > java.util.concurrent.ConcurrentHashMap.java > Line 314 openjdk> jdk8> java.util.concurrent.ConcurrentHashMap.java > 314行


If class Segment is unused and also field like DEFAULT_CONCURRENCY_LEVEL is also unused - why not get rid of it from implementation - is it for some historical reasons? 如果类Segment未使用,并且DEFAULT_CONCURRENCY_LEVEL类的字段也未使用 - 为什么不从实现中删除它 - 是否出于某些历史原因?

To ensure serialization compatibility. 确保序列化兼容性。

We also declare an unused Segment class that is instantiated in minimal form only when serializing. 我们还声明了一个未使用的Segment类,它仅在序列化时以最小的形式实例化。

openjdk > jdk8 > java.util.concurrent.ConcurrentHashMap.java > Line 486 openjdk> jdk8> java.util.concurrent.ConcurrentHashMap.java > 486行

 /** * The default concurrency level for this table. Unused but * defined for compatibility with previous versions of this class. */ private static final int DEFAULT_CONCURRENCY_LEVEL = 16; 

openjdk > jdk8 > java.util.concurrent.ConcurrentHashMap.java > Line 526 openjdk> jdk8> java.util.concurrent.ConcurrentHashMap.java >第526行

 /** * Stripped-down version of helper class used in previous version, * declared for the sake of serialization compatibility */ static class Segment<K,V> extends ReentrantLock implements Serializable { private static final long serialVersionUID = 2249069246763182397L; final float loadFactor; Segment(float lf) { this.loadFactor = lf; } } 

openjdk > jdk8 > java.util.concurrent.ConcurrentHashMap.java > Line 1366 openjdk> jdk8> java.util.concurrent.ConcurrentHashMap.java > 1366行


If we are not locking on segments, like it used to be for Java version < 7, is locking only on specific node sufficient? 如果我们没有锁定段,就像以前的Java版本<7那样,只能在特定节点上锁定吗?

No. 没有。

Using the first node of a list as a lock does not by itself suffice though: When a node is locked, any update must first validate that it is still the first node after locking it, and retry if not. 使用列表的第一个节点作为锁定本身就不够了:当一个节点被锁定时,任何更新必须首先验证它在锁定后仍然是第一个节点,如果没有则重试。 Because new nodes are always appended to lists, once a node is first in a bin, it remains first until deleted or the bin becomes invalidated (upon resizing). 由于新节点始终附加到列表中,因此一旦节点首先位于bin中,它将一直保留,直到删除或bin变为无效(调整大小时)。

openjdk > jdk8 > java.util.concurrent.ConcurrentHashMap.java > Line 320 openjdk> jdk8> java.util.concurrent.ConcurrentHashMap.java > 320行

What is the cause of such significant change in ConcurrentHashMap implementation? ConcurrentHashMap实现中这种重大变化的原因是什么?

ConcurrentHashMap.java#l272 : ConcurrentHashMap.java#l272

* The primary design goal of this hash table is to maintain
* concurrent readability (typically method get(), but also
* iterators and related methods) while minimizing update
* contention.

Segment class remains unused because of compatibility reasons, ConcurrentHashMap.java#l481 : 由于兼容性原因, Segment类仍未使用, ConcurrentHashMap.java#l481

* Maintaining API and serialization compatibility with previous
* versions of this class introduces several oddities. Mainly:
* [...]
* We also declare an unused "Segment" class that is
* instantiated in minimal form only when serializing.

... is locking only on specific node sufficient? ...仅在特定节点上锁定是否足够? If yes - why? 如果是 - 为什么?

ConcurrentHashMap.java#l320 : ConcurrentHashMap.java#l320

* Using the first node of a list as a lock does not by itself
* suffice though: When a node is locked, any update must first
* validate that it is still the first node after locking it,
* and retry if not.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM