摘要:如下代碼省略相關代碼省略相關代碼可以看到在里面,是直接采用數組鏈表紅黑樹來實現,時間復雜度在和之間,如果鏈表轉化為紅黑樹了,那么就是到。
在JDK1.8里面,ConcurrentHashMap在put方法里面已經將分段鎖移除了,轉而是CAS鎖和synchronized
ConcurrentHashMap是Java里面同時兼顧性能和線程安全的一個鍵值對集合,同屬于鍵值對的集合還有HashTable以及HashMap,
HashTable是一個線程安全的類,因為它的所有public方法都被synchronized修飾,這樣就導致了一個問題,就是效率太低。
雖然HashMap在JDK1.8的并發場景下觸發擴容時不會出現成環了,但是會出現數據丟失的情況。
所以如果需要在多線程的情況下(多讀少寫))使用Map集合的話,ConcurrentHashMap是一個不錯的選擇。
ConcurrentHashMap在JDK1.8的時候將put()方法中的分段鎖Segment移除,轉而采用一種CAS鎖和synchronized來實現插入方法的線程安全。
如下代碼:
/** Implementation for put and putIfAbsent */ final V putVal(K key, V value, boolean onlyIfAbsent) { //省略相關代碼 for (Node[] tab = table;;) { Node f; int n, i, fh; if (tab == null || (n = tab.length) == 0) tab = initTable(); else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { if (casTabAt(tab, i, null, new Node (hash, key, value, null))) break; // no lock when adding to empty bin } else if ((fh = f.hash) == MOVED) tab = helpTransfer(tab, f); else { V oldVal = null; synchronized (f) { //省略相關代碼 } if (binCount != 0) { if (binCount >= TREEIFY_THRESHOLD) treeifyBin(tab, i); if (oldVal != null) return oldVal; break; } } } addCount(1L, binCount); return null; }
可以看到在JDK1.8里面,ConcurrentHashMap是直接采用數組+鏈表+紅黑樹來實現,時間復雜度在O(1)和O(n)之間,如果鏈表轉化為紅黑樹了,那么就是O(1)到O(nlogn)。
在這里值得一提的是,ConcurrentHashMap會判斷tabAt(tab, i = (n - 1) & hash)是不是 null,是的話就直接采用CAS進行插入,而如果不為空的話,則是synchronized鎖住當前Node的首節點,這是因為當該Node不為空的時候,證明了此時出現了Hash碰撞,就會涉及到鏈表的尾節點新增或者紅黑樹的節點新增以及紅黑樹的平衡,這些操作自然都是非原子性的。
從而導致無法使用CAS,當Node的當前下標為null的時候,由于只是涉及數組的新增,所以用CAS即可。
因為CAS是一種基于版本控制的方式來實現,而碰撞之后的操作太多,所以直接用synchronized比較合適。ConcurrentHashMap在迭代時和HashMap的區別
當一個集合在迭代的時候如果動態的添加或者刪除元素,那么就會拋出Concurrentmodificationexception,但是在ConcurrentHashMap里面卻不會,例如如下代碼:
public static void main(String[] args) { Mapmap = new ConcurrentHashMap (); map.put("a","a1"); map.put("b","b1"); map.put("c","c1"); map.put("d","d1"); map.put("e","e1"); Iterator iterator = map.keySet().iterator(); while (iterator.hasNext()){ String it = iterator.next(); if("b".equals(it)){ map.remove("d"); } System.out.println(it); } } 控制臺打印如下: a b c e
而當你把ConcurrentHashMap換成HashMap的時候,控制臺就會拋出一個異常:
Exception in thread "main" a b java.util.ConcurrentModificationException at java.util.HashMap$HashIterator.nextNode(HashMap.java:1442) at java.util.HashMap$KeyIterator.next(HashMap.java:1466) at xyz.somersames.ListTest.main(ListTest.java:22)
原因在于ConcurrentHashMap的next方法并不會去檢查modCount和expectedModCount,但是會檢查下一個節點是不是為空
if ((p = next) == null) throw new NoSuchElementException();
當我們進行remove的時候,ConcurrentHashMap會直接通過修改指針的方式來進行移除操作,同樣的,也會鎖住數組的頭節點直至移除結束,所以在同一個時刻,只會有一個線程對當前數組下標的所有節點進行操作。
但是在HashMap里面,next方法會進行一個check,而remove操作會修改modCount,導致modCount和expectedModCount不相等,所以就會導致
ConcurrentModificationException
稍微修改下代碼:
public static void main(String[] args) { Map并發下的處理map = new ConcurrentHashMap (); map.put("a","a1"); map.put("b","b1"); map.put("c","c1"); map.put("d","d1"); map.put("e","e1"); Iterator iterator = map.keySet().iterator(); while (iterator.hasNext()){ if("b".equals(iterator.next())){ map.remove("d"); } System.out.println(iterator.next()); } } 控制臺打印如下: b d Exception in thread "main" java.util.NoSuchElementException at java.util.concurrent.ConcurrentHashMap$KeyIterator.next(ConcurrentHashMap.java:3416) at com.xzh.ssmtest.ListTest.main(ListTest.java:25)
由于每一個Node的首節點都會被synchronized修飾,從而將一個元素的新增轉化為一個原子操作,同時Node的value和next都是由volatile關鍵字進行修飾,從而可以保證可見性。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74650.html
摘要:下面我來簡單總結一下的核心要點底層結構是散列表數組鏈表紅黑樹,這一點和是一樣的。是將所有的方法進行同步,效率低下。而作為一個高并發的容器,它是通過部分鎖定算法來進行實現線程安全的。 前言 聲明,本文用的是jdk1.8 前面章節回顧: Collection總覽 List集合就這么簡單【源碼剖析】 Map集合、散列表、紅黑樹介紹 HashMap就是這么簡單【源碼剖析】 LinkedHas...
摘要:最近準備面試,一談到基礎,大部分面試官上來就數據結構素質三連與區別,底層數據結構,為什么能保證線程安全。數組順序存儲,內存連續,查詢快,插入刪除效率稍微低,不過現在略有改善。而在開始,是由和的方式去實現高并發下的線程安全。 最近準備面試,一談到java基礎,大部分面試官上來就java數據結構素質三連:ArrayList與LinkedList區別,HashMap底層數據結構,Concur...
摘要:若遇到哈希沖突,則將沖突的值加到鏈表中即可。之后相比于之前的版本,之后在解決哈希沖突時有了較大的變化,當鏈表長度大于閾值默認為時,將鏈表轉化為紅黑樹,以減少搜索時間。有序,唯一紅黑樹自平衡的排序二叉樹。 本文是最最最常見Java面試題總結系列第三周的文章。主要內容: Arraylist 與 LinkedList 異同 ArrayList 與 Vector 區別 HashMap的底層...
ConcurrentHashMap源碼分析_JDK1.8版本 聲明 文章均為本人技術筆記,轉載請注明出處[1] https://segmentfault.com/u/yzwall[2] blog.csdn.net/j_dark/ JDK1.6版本 ConcurrentHashMap結構 showImg(https://segmentfault.com/img/remote/146000000900...
閱讀 2530·2023-04-26 02:57
閱讀 1412·2023-04-25 21:40
閱讀 2178·2021-11-24 09:39
閱讀 3566·2021-08-30 09:49
閱讀 765·2019-08-30 15:54
閱讀 1173·2019-08-30 15:52
閱讀 2080·2019-08-30 15:44
閱讀 1278·2019-08-28 18:27