簡體   English   中英

Java 8+ ConcurrentHashMap鎖定條帶化

[英]Java 8+ ConcurrentHashMap lock striping

我一直在讀Brian Goetz的Concurency in Practice

在關於鎖定條帶的章節中,寫入ConcurrentHashMap使用16個桶來改進許多線程的多線程訪問:

鎖分裂有時可以擴展到一組變量獨立對象的分區鎖定,在這種情況下,它被稱為鎖定條帶。 例如,ConcurrentHashMap的實現使用一個包含16個鎖的數組,每個鎖保護1/16的散列桶; 鏟斗N由鎖定N mod 16保護。

我讀過這些問題:

ConcurrentHashMap鎖定

需要簡單解釋“鎖定條帶化”如何與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實現中的任何位置調用)。

  1. ConcurrentHashMap實現中這種重大變化的原因是什么?

  2. 如果類Segment未使用,並且DEFAULT_CONCURRENCY_LEVEL類的字段也未使用 - 為什么不從實現中刪除它 - 是否出於某些歷史原因?

  3. 如果我們沒有鎖定段,就像以前的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實現中這種重大變化的原因是什么?

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類仍未使用, 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.

...僅在特定節點上鎖定是否足夠? 如果是 - 為什么?

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