上文說了HashMap,其實HashMap是線程非安全的,JDK里面有個線程安全的就是HashTable,查看HashTable每個方法都增加了synchronized同步鎖,也就說每次只能進(jìn)入一個線程,這樣影響效率。JDK源碼也推薦使用ConcurrentHashMap。

推理ConcurrentHashMap的實現(xiàn)1.8
- ① JDK的描述
If a thread-safe implementation is not needed, it is recommended to use HashMap in place of code Hashtable. If a thread-safe highly-concurrent implementation is desired, then it is recommended to use ConcurrentHashMap in place of code Hashtable.
如果你不需要線程安全,那么使用HashMap,如果需要線程安全,那么使用ConcurrentHashMap。HashTable已經(jīng)被淘汰了,不要在新的代碼中再使用它。
- ② 描述
和 HashMap 非常類似,唯一的區(qū)別就是其中的核心數(shù)據(jù)如 value ,以及鏈表都是 volatile 修飾的,保證了獲取時的可見性。原理上來說:根據(jù) key 計算出 hashcode ,判斷是否需要進(jìn)行初始化,f 即為當(dāng)前 key 定位出的 Node,如果為空表示當(dāng)前位置可以寫入數(shù)據(jù),利用 CAS 嘗試寫入,多個線程操作只有一個成功,失敗則自旋保證成功。如果當(dāng)前位置的 hashcode == MOVED == -1,則需要進(jìn)行擴(kuò)容。如果都不滿足,則利用 synchronized 鎖寫入數(shù)據(jù)。,如果數(shù)量大于 TREEIFY_THRESHOLD 則要轉(zhuǎn)換為紅黑樹。。Mysql里面有表鎖和行鎖的概念。表鎖就是HashTable,行鎖就是ConcurrentHashMap 。

- ③ JDK1.8中的ConcurrentHashMap原理分析
1.7 已經(jīng)解決了并發(fā)問題,并且能支持 N 個 Segment 這么多次數(shù)的并發(fā),但依然存在 HashMap 在 1.7 版本中的問題。那么是什么問題呢?很明顯那就是查詢遍歷鏈表效率太低。
因此 1.8 做了一些數(shù)據(jù)結(jié)構(gòu)上的調(diào)整。,在 JAVA8 中它摒棄了 Segment(鎖段)的概念,而是啟用了一種全新的方式實現(xiàn),利用 CAS 算法。底層依然由“數(shù)組”+鏈表+紅黑樹的方式思想,但是為了做到并發(fā),又增加了很多輔助的類,例如 TreeBin、Traverser等對象內(nèi)部類。如何讓多線程之間,對象的狀態(tài)對于各線程的“可視性”是順序一致的:ConcurrentHashMap 使用了 happensbefore 規(guī)則來實現(xiàn)。 happens-before規(guī)則(摘取自 JAVA 并發(fā)編程):
程序次序法則
線程中的每個動作A都 happens-before 于該線程中的每一個動作B,其中,在程序中,所有的動作B都能出現(xiàn)在A之后。
監(jiān)視器鎖法則
對一個監(jiān)視器鎖的解鎖 happens-before 于每一個后續(xù)對同一監(jiān)視器鎖的加鎖。
volatile 變量法則
對 volatile 域的寫入操作 happens-before 于每一個后續(xù)對同一個域的讀寫操作。
線程啟動法則
在一個線程里,對 Thread.start 的調(diào)用會 happens-before 于每個啟動線程的動作。
線程終結(jié)法則
線程中的任何動作都 happens-before 于其他線程檢測到這個線程已經(jīng)終結(jié)、或者從 Thread.join 調(diào)用中成功返回,或 Thread.isAlive 返回 false。
中斷法則
一個線程調(diào)用另一個線程的 interrupt happens-before 于被中斷的線程發(fā)現(xiàn)中斷。
終結(jié)法則
一個對象的構(gòu)造函數(shù)的結(jié)束 happens-before 于這個對象 finalizer 的開始。
傳遞性
如果 A happens-before 于 B,且 B happens-before 于 C,則 A happens-before于C:
假設(shè)代碼有兩條語句,代碼順序是語句1先于語句2執(zhí)行;那么只要語句之間不存在依賴關(guān)系,那么打亂它們的順序?qū)ψ罱K的結(jié)果沒有影響的話,那么真正交給CPU去執(zhí)行時,他們的執(zhí)行順序可以是先執(zhí)行語句2然后語句1。
PS:不管是1.7的hashMap還是ConcurrentHashMap,源碼的可讀性變差。目前基本都是jdk1.8就沒有說1.7的事情,畢竟事務(wù)都是在進(jìn)化的。里面用到了很多數(shù)據(jù)結(jié)構(gòu),數(shù)據(jù)結(jié)構(gòu)說難也不難,說容易也不容易,它本身就是人的思維的一種體現(xiàn)。好像說走路方式一樣,怎么走都可以到指定的地方,但是方法不一樣,數(shù)據(jù)結(jié)構(gòu)就是通過更加科學(xué)方式來進(jìn)行,歸根到底還是看我的【數(shù)據(jù)結(jié)構(gòu)與算法】專題。數(shù)據(jù)結(jié)構(gòu)給你,通過算法來進(jìn)行查找,如果是遍歷的方式來查,可能相對于hash的形式要差一些。