日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

聊聊ConcurrentHashMap

發布時間:2024/8/1 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 聊聊ConcurrentHashMap 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

聊聊ConcurrentHashMap

一、為什么需要ConcurrentHashMap

1、HashMap 線程不安全

在多線程環境下,使用HashMap進行 put 操作的時候可能造成死循環,導致 CPU 使用率太高

為什么HashMap線程不安全?

在 put 的時候,插入元素超過了容量,就會進行rehash,這個會重新將原數組的內容重新hash到新的擴容數組中,在多線程的環境下,存在同時其他的元素也在進行 put 操作,如果 hash 值相同,可能出現同時在同一數組下用鏈表表示,造成閉環,導致在 get 時會出現死循環,所以不安全

2、HashTable 安全但是效率太低了

因為 HashTable 是利用 synchronized 來保證線程安全的,在線程競爭激烈的情況下效率將會非常低。因為在一個線程訪問同步方法的時候,其他線程只能阻塞等待。

二、ConcurrentHashMap 的 好處

實現了前面的兩個問題 :線程安全了 并且也解決了HashTable 效率低下的問題

三、怎么解決效率低下的問題

這個主要是運用了分段鎖(segment)的思想

HashTable 為什么效率低,就是因為它是多個線程競爭同一把鎖,那么如果容器里面有很多把鎖,這個問題是不是就可以解決了,這個就是ConcurrentHashMap所使用的分段鎖技術。

首先將數據分為一段一段的進程存儲,然后給每一段分別加上鎖,當一個線程占用鎖訪問其中一個段的數據的時候,其他段的數據也可以被其他線程訪問。

接下來我們就重點講講分段鎖吧

ConcurrentHashMap 是由Segment 數組結構和HashEntry數組結構組成的。Segment是一種可重入鎖ReentrantLock,在 ConcurrentHashMap 里面扮演鎖的角色,HashEntry 則是用來村塾鍵值對數據。一個 ConcurrentHashMap 里面包含一個 Segment數組,Segment的結構和HashMap類似,是一種數組+鏈表結構,一個Segment里面包含一個 HashEntry數組,每個HashEntry是一個鏈表節點構成的元素,每個Segment守護一個HashEntry數組里面的元素,當對HashEntry數組的元素進行修改時,必須首先獲得它對應的Segment鎖。可以說,ConcurrentHashMap是一個二級的哈希表。在一個總的哈希表下面還有若干個子哈希表。

四、采用分段鎖技術的好處:并發的讀寫

case1:不同Segment的并發寫入:

不同的Segment的寫入是可以并發執行的

case 2:同一個Segment 的寫

Segment的寫入是需要上鎖的,因此對同一個Segment的并發寫會被阻塞

case 3:同一個Segment 的寫-讀

同一個Segment的寫-讀是可以并發執行的

五、詳細看看 讀-寫 的過程

1、讀:Get()

  • 為輸入的Key做 Hash 運算,得到 hash 值
  • 通過 hash 值,定位到對應的Segment 對象
  • 再次通過 hash 值,定位到 Segment 當中數組的具體位置

讀操作其實是沒有鎖的,第一次通過 hash 定位到 Segment 上,第二次通過 hash 定位到 具體元素上。因為 hashEntry 中的 value 屬性是用 volatile 修飾的,保證了可見性,所以每次獲取的都死最新值。

2、寫:Put()

  • 為輸入的 Key 做 Hash 運算,得到 hash 值
  • 通過 hash 值,定位到對應的 Segment 對象
  • 獲取可重入鎖
  • 再次通過 hash 值,定位到 Segment 當中數組的具體位置
  • 插入或覆蓋 HashEntry 對象
  • 釋放鎖

總結:可以看出ConcurrentHashMap在讀寫的時候都需要兩次定位。首先是定位到 Segment ,然后再定位到 Segment 下的具體的數組下標

六、size() 怎么 解決一致性問題

size()目的是統計ConcurrentHashMap的總元素數量,自然需要把各個Segment 內部的元素都加起來。但是在統計數量的時候,有可能 已經統計過的Segment順佳插入了新的元素,這個時候應該怎么辦?下面我們來看看ConcurrentHashMap的size(),他是一個嵌套循環,大致邏輯如下:

  • 遍歷所有的 Segment
  • 把 Segment 的元素數量累加起來
  • 把 Segment 的修改次數累加起來
  • 判斷所有 Segment 的總修改次數是否大于上一次的總修改次數。如果大于,說明統計過程中有修改,重新統計,嘗試次數+1;如果不是,說明沒有修改,統計結束
  • 如果嘗試次數超過閾值,則對每一個 Segment 加鎖,在重新統計
  • 再次判斷所有 Segment 的總修改次數是否大于上一次的總修改次數。由于已經加鎖,次數一定和上一次相同
  • 釋放鎖,統計結束
  • 總結:一開始對 Segment 不加鎖,而是直接嘗試將所有的 Segment 元素中的count相加,這樣執行兩次,然后將兩次的結果進行對比,如果兩次結果相同則直接返回;如果不相同,則將所有的Segment加鎖,然后再次執行統計得到對應的 size 值。

    七、再看看1.8是怎么實現的

    1.8 拋棄了分段鎖(Segment),而是采取的是 CAS + Synchronized 的方式

    將1.7里面的 HashEntry 改為 Node ,但是作用是一樣的。

    先看看一個概念:樂觀鎖和悲觀鎖

    悲觀鎖:認為對于同一個數據的并發操作,一定是為發生修改的

    樂觀鎖:對于同一個數據的并發操作是不會發生修改的,在更新的時候會采取嘗試更新不斷嘗試的方式更新數據

    CAS(compare and swap,比較交換)原理:

    CAS有三個操作數,內存值V、預期值A、要修改的值B,當且僅當A和V相等時才會將V修改為B,否則什么也不會做。

    CAS的缺點:

    存在 ABA 問題,解決辦法,添加版本號

    循環時間長,開銷大

    只能保證一個共享變量的原子操作

    總結

    以上是生活随笔為你收集整理的聊聊ConcurrentHashMap的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。