![](/img/trans.png)
[英]Need simple explanation how “lock striping” works with ConcurrentHashMap
[英]Java 8+ ConcurrentHashMap lock striping
我一直在讀Brian Goetz的Concurency in Practice
。
在關於鎖定條帶的章節中,寫入ConcurrentHashMap
使用16個桶來改進許多線程的多線程訪問:
鎖分裂有時可以擴展到一組變量獨立對象的分區鎖定,在這種情況下,它被稱為鎖定條帶。 例如,ConcurrentHashMap的實現使用一個包含16個鎖的數組,每個鎖保護1/16的散列桶; 鏟斗N由鎖定N mod 16保護。
我讀過這些問題:
需要簡單解釋“鎖定條帶化”如何與ConcurrentHashMap一起使用
但是,這些答案對Java版本<= 7有效。
對於Java 8+,行為似乎發生了顯着變化。 對於Java 8+,似乎鎖定不是針對Segment而是針對表中的特定節點而獲取的( transient volatile ConcurrentHashMap.Node<K, V>[] table;
)。 例如對於putVal
操作:
ConcurrentHashMap.Node var7;
.... ///retrive node for var7
synchronized(var7) {
....
}
而且從Java8 +字段開始,如DEFAULT_CONCURRENCY_LEVEL
和類Segment
似乎在實現中未使用(它僅在私有方法writeObject::ObjectOutputStream
並且此方法不會在ConcurrentHashMap
實現中的任何位置調用)。
ConcurrentHashMap
實現中這種重大變化的原因是什么?
如果類Segment
未使用,並且DEFAULT_CONCURRENCY_LEVEL
類的字段也未使用 - 為什么不從實現中刪除它 - 是否出於某些歷史原因?
如果我們沒有鎖定段,就像以前的Java版本<7那樣,只能在特定節點上鎖定嗎? 如果是 - 為什么? 這是否意味着我們不需要鎖定條帶?
ConcurrentHashMap實現中這種重大變化的原因是什么?
減少內存占用(原因之一) 。
我們不想浪費將不同鎖對象與每個bin關聯所需的空間,因此使用bin列表本身的第一個節點作為鎖。 鎖定對這些鎖的支持依賴於內置的“同步”監視器。
openjdk> jdk8>
java.util.concurrent.ConcurrentHashMap.java
> 314行
如果類
Segment
未使用,並且DEFAULT_CONCURRENCY_LEVEL
類的字段也未使用 - 為什么不從實現中刪除它 - 是否出於某些歷史原因?
確保序列化兼容性。
我們還聲明了一個未使用的
Segment
類,它僅在序列化時以最小的形式實例化。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
>第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
> 1366行
如果我們沒有鎖定段,就像以前的Java版本<7那樣,只能在特定節點上鎖定嗎?
沒有。
使用列表的第一個節點作為鎖定本身就不夠了:當一個節點被鎖定時,任何更新必須首先驗證它在鎖定后仍然是第一個節點,如果沒有則重試。 由於新節點始終附加到列表中,因此一旦節點首先位於bin中,它將一直保留,直到刪除或bin變為無效(調整大小時)。
openjdk> jdk8>
java.util.concurrent.ConcurrentHashMap.java
> 320行
ConcurrentHashMap
實現中這種重大變化的原因是什么?
* 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
類仍未使用, 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.
...僅在特定節點上鎖定是否足夠? 如果是 - 為什么?
* 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.