項目中經常會有多個線程要訪問同一個數據,此時比較常用的辦法是用synchronize加鎖,CAS去進行安全的累加,去實現多線程場景下的安全的更新一個數據的效果,HashMap是用的比較多,可能就是多個線程同時讀寫一個HashMap,HashMap是線程不安全的,如果對整個map去synchronized加鎖,類似下面這種偽代碼也沒那麼必要。
synchronized(map){
}
這時候JDK就退出了線程安全的ConcurrentHashMap,那它是怎麼實現線程安全的呢?
HashMap底層本身就是一個大數組,JDK1.8以前是將大數組分成多個數組,分段加鎖,一個數組一個鎖。比如大數組分成[數組1],[數組2],[數組3] -->每個數組都對應一個鎖。多個線程過來,線程1要put的位置是數組1的第3個index的位置,線程2要put的位置是數組2的第5個index的位置,這兩個線程在不同的數組裡面放數據本身不存在線程安全問題就不需要加鎖。
JDK1.8以後,做了一些優化和改進,優化了鎖的細粒度,還是一個大數組,對數組中每個元素進行CAS。
[一個大數組]數組裡對每個元素進行put操作,都是有一個不同的鎖,剛開始進行put的時候,如果兩個線程都是在數組的第3個index位置進行put,這個時候採取的是CAS的策略,同一個時間只有一個線程能成功執行這個CAS,就是說他剛開始先獲取一個數組的第3個index位置的值,如果為null然後執行CAS,線程1比較一下達到預期就是put進去我的這條數據,同一時間其他線程執行CAS都會失敗。
通過對數組裡每個元素執行CAS的策略,會有一個明顯的好處,就是如果很多線程對數組裡不同的元素執行put,大家是沒有關係的不存在線程安全問題。如果其他人CAS失敗了,其他人此時會發現說該位置已經給剛才其他的人put進去值了,這個時候就需要在這個位置基於連表+紅黑樹來進行處理,synchronized(數組[3]),加鎖,然後put進去自己的數據。
對JDK1.8之後ConcurrentHashMap的一點小總結,如果是對數組裡同一個位置的元素進行並發的操作,就需要加鎖串行化處理,如果是對數組不同位置進行操作,大家就可以並發的處理。