[英]Why ConcurrentHashMap cannot have a lock for each bucket?
我們知道,java 的 ConcurrentHashMap 有許多內部鎖,每個鎖都保護着存儲桶數組的某個區域。
一個問題是:為什么我們不能為每個桶創建一個鎖?
已經問過一個類似的問題: Java ConcurrentHashMap 中增加分區數量的缺點?
根據回答,有以下幾個原因:
同時運行的最大線程數受處理器內核數的限制。 這樣對嗎? 我們是否可以始終聲明,如果我們有 8 核處理器,我們在 ConcurrentHashMap 中不需要超過 8 個鎖定區域?
浪費了 L2 緩存。 為什么?
存在內存浪費。 看起來這是因為額外的鎖創建。
還有其他原因嗎?
希望我能很好地解釋……此刻有點匆忙……
你的第一個問題的答案:
“為什么我們不能為每個存儲桶創建一個鎖?”
是您可以為每個存儲桶創建一個鎖——這不一定是最好的行動方案。
你的問題的答案:
“我們能否始終聲明,如果我們有 8 核處理器,我們不需要在 ConcurrentHashMap 中超過 8 個鎖定區域”
從技術上講是“否”,盡管這取決於您所說的“需要”是什么意思。 擁有多個與您的系統的最大並發數相匹配或稍大的區域不一定能防止爭用,但在實踐中它工作得很好。 沒有什么可以阻止兩個線程同時嘗試訪問同一個區域,即使還有其他區域沒有被鎖定。
通過在 8 核處理器上擁有 8 個或更多區域,您可以保證可以同時訪問所有區域而不會發生爭用。 如果您有 8 個內核(不是超線程),您最多可以同時執行 8 個操作。 即便如此,理想的區域數量(例如 16 個)也可能比核心數量多,因為它會以較低的成本(僅 8 個額外的鎖)減少爭用的可能性。
正如JavaDoc 中提到的那樣,隨着區域數量相對於最大並發數的增加,擁有額外區域的好處最終會減少,這導致它們浪費空間(內存)。 這是爭用可能性(給定一個區域上的鎖,另一個線程嘗試訪問它的可能性有多大)和浪費空間之間的平衡。
還有一些其他因素會影響ConcurrentHashMap
性能:
無論有多少個區域,所有這三件事都會對性能產生積極或消極的影響,並可能降低區域數量的相關性。 由於它們發揮着重要作用,因此它們使擁有更多區域的總體上對您有所幫助的可能性降低。 由於您只能同時執行這么多線程,因此擁有快速完成工作並釋放鎖的線程是更好的關注點。
至於您關於緩存的問題:老實說,我不確定,但我可以猜測一下。 當您大量使用地圖時,這些鎖最終會出現在緩存上並占用空間,可能會破壞其他可能更有用的東西。 緩存比主存稀缺得多,緩存未命中會浪費大量時間。 我認為這里的想法是普遍厭惡將很多東西放在緩存中,而這些東西不會帶來顯着的好處。 極端情況:如果緩存中充滿了鎖(不知何故)並且每個數據調用都傳到內存中,那么您的性能就會受到影響。
我們是否可以始終聲明,如果我們有 8 核處理器,我們在 ConcurrentHashMap 中不需要超過 8 個鎖定區域?
不,這是完全錯誤的。 它取決於兩個因素,線程數(並發)和段沖突數。 如果兩個線程競爭同一段,一個線程可能會阻塞另一個。
雖然擁有內核的線程數只能與內核數一樣多,但上述語句的一個大錯誤是假設不在內核上運行的線程不能擁有鎖。 但是擁有鎖的線程仍然可以在下一個線程的任務切換上釋放 CPU,然后在嘗試獲取相同的鎖時被阻塞。
但是根據核心數調整線程數並不罕見,尤其是對於計算密集型任務。 因此ConcurrentHashMap
的並發級別間接取決於典型設置中的內核數量。
每個桶都有一個鎖意味着為每個桶維護一個鎖狀態和一個等待隊列,這意味着相當多的資源。 請記住,只有並發寫操作需要鎖,讀線程不需要。
但是,對於 Java 8 實現,這種考慮已經過時。 它使用無等待算法進行桶更新,至少對於沒有沖突的桶是這樣。 這有點像每個桶都有一個鎖,因為在不同桶上運行的線程不會相互干擾,但沒有維護鎖狀態和等待隊列的開銷。 唯一需要關心的是給地圖一個合適的初始大小。 因此,如果指定了concurrencyLevel
,則將用作初始大小調整提示,否則將被忽略。
Java 8 的ConcurrentHashMap確實在每個存儲桶上加鎖。 寫入鎖定,但可能發生並發讀取。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.