為什么要使用ConcurrentHashMap


好久沒寫過技術性文章了,還是要堅持下去。掌握的知識,能寫出來或者是講給別人聽才是真正的掌握了知識,如果不善於給別人講,實際上還是沒有真正掌握相關的知識,挑個簡單的寫吧。
面試的時候經常會被問到hashmap和hashtable的區別。心里就開始鄙視這個面試的人了,不要拿這種基礎的問題來為難一個抗戰都快勝利的碼農,那些個條條框框誰xx記得住。可是,遇到牛逼點的單位要問我hashmap結構、算法、這一系列牽扯到移位、與、或、非、異或2進制16進制這些我也真的答不上來,汗。。。下來惡補一下吧。
這時候,可以跟他噴這些,
Hashtable為什么性能低?
HashMap高並下發會發生什么?
HashMap內部結構是什么?
ConcurrentHashmap為什么可以支持高並發?為什么比hashmap讀取效率高?
 
hashtable從java1.0就引入了,早年在深圳寫java的時候,項目的全局變量里清一色的hashtable,也不了解為什么,用就行了,反正都是賣幾個億的項目,也輪不到自己操心,有時候還要鄙視一下旁邊坐的華為的從c轉來做java的哥們,還沒我知道多。呵呵,自己就是典型的只了解業務不了解原理的碼農,當年要跟着大牛混就好了,無奈,身邊都是碼農。
隨着時間的推移,自己也知道主動去了解一些知識,例如hashmap寫入慢,讀取快;linkedhashmap是個有序鏈表讀取慢,寫入快;treeMap可以幫你自動做好升序排列。全局變量就是拿來讀着用的嘛,得!發現用hashmap比hashtable性能好,還鄙視當年做項目時候寫全局變量的人,隨后,自己動手開發的項目全局變量都用起了hashmap。嗯,用着還挺好,也沒發現什么問題,呵呵,都是不值錢的小項目,撐死幾十個人的並發量,能有什么問題。
終於有一天,boss開始吐槽,你的程序cpu占用率怎么那么高呢,我不以為然,並發量高么,證明咱們業務量大呀,你應該高興才對。我的數據都在內存里,速度快着呢,估計是循環和job太多了吧,boss一臉懵逼,似懂非懂,反正跟你說也不懂,出於程序員的本能,我還是檢查了一遍日志,不好,日志有錯誤!
目標直指hashmap全局變量,怎么會這樣呢,怎么說也搞了這么多年了,第一反應就是,入坑了,hashmap不是線程安全的,光顧着讀取快了,沒料到這個全局變量隨時會多線程讀寫,為何會出現cpu占用率高呢,有問題,找谷哥,什么,用度娘,算了吧,除了賣假葯的,屁都搜不出來。來看看為何cpu會占用率高,因為HashMap以鏈表組形式存在,初始數組16位(為何16位,又是一堆移位算法,下一篇文章再寫吧),如果長度超過75%,長度增加一倍,多線程操作的時候,恰巧兩個線程插入的時候都需要擴容,形成了兩個鏈表,這時候讀取,size不一樣,報錯了。其實這時候報錯都是好事,至少不會陷入死循環讓cpu死了,有種情況,假如兩個線程在讀,還有個線程在寫,恰巧擴容了,這時候你死都不知道咋死的,直接就是死循環,假如你是雙核cpu,cpu占用率就是50%,兩個線程恰巧都進入死循環了,得!中獎了。
這該咋辦,換成Hashtable吧,這點小坑難不住我,想起原來以前深圳的boss系統,其實每一行核心代碼都不是隨便寫的,其實這個問題很早sun的大爺們都發現了,大爺們認為HashMap不是bug,而是使用場景有要求,單線程讀取操作,又快又省空間。
難道我寫了半天就為了把hashmap換成hashtable,那也太浪費鍵盤了,我今天重點說的是ConcurrentHashMap,聽名字就很牛氣,並發的hashmap。其實早在jdk1.5,大約2010年左右,sun的大爺們就推出了ConcurrentHashMap,線程安全,讀寫還快。你這不是蒙我呢,線程安全和讀寫速度肯定是成反比的,怎么可能。看了源碼才知道,這是一種以空間換時間的結構,跟分布式緩存結構有點像,創建的時候,內存直接分為了16個segment,每個segment實際上還是存儲的哈希表,寫入的時候,先找到對應的segment,然后鎖這個segment,寫完,解鎖,汗!就這么簡單解決了,鎖segment的時候,其他segment還可以繼續工作。好像聽着挺簡單的,其實大爺們的代碼看着真的很頭疼,到處都是移位、與或非,就拿計算存放位置的代碼來看,如何均勻的散列,減少位置碰撞都是有講究的,還是得感嘆你大爺就是你大爺。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM