ConcurrentHashMap

HashMap是我们用得非常频繁的一个集合,但是它是线程不安全的。并且在多线程环境下,put操作是有可能产生死循环,不过在JDK1.8的版本中更换了数据插入的顺序,已经解决了这个问题。

为了解决该问题,提供了Hashtable和Collections.synchronizedMap(hashMap)两种解决方案,但是这两种方案都是对读写加锁,独占式。一个线程在读时其他线程必须等待,吞吐量较低,性能较为低下。而J.U.C给我们提供了高性能的线程安全HashMap:ConcurrentHashMap。

在1.8版本以前,ConcurrentHashMap采用分段锁的概念,使锁更加细化,但是1.8已经改变了这种思路,而是利用CAS+Synchronized来保证并发更新的安全,当然底层采用数组+链表+红黑树的存储结构。

HashMap 是最简单的,它不支持并发操作,下面这张图是 HashMap 的结构:

HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。每个绿色的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next。

public HashMap(int initialCapacity, float loadFactor) 初始化方法的参数说明:

put 过程

get过程

ConcurrentHashMap 和 HashMap 思路是差不多的,但是因为它支持并发操作,所以要复杂一些。

整个 ConcurrentHashMap 由一个个 Segment 组成,Segment 代表”部分“或”一段“的意思,所以很多人都会将其描述为分段锁。简单的说,ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承 ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的。

再具体到每个 Segment 内部,其实每个 Segment 很像之前介绍的 HashMap,每次操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的。

初始化

public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) 初始化方法

举个简单的例子:

用 new ConcurrentHashMap() 无参构造函数进行初始化的,那么初始化完成后:

put过程

get过程

Java8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑树 组成。

根据 Java7 HashMap 的介绍,我们知道,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度。

为了降低这部分的开销,在 Java8 中,当链表中的元素超过了 8 个以后,会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度。

jdk7 中使用 Entry 来代表每个 HashMap 中的数据节点,jdk8 中使用 Node,基本没有区别,都是 key,value,hash 和 next 这四个属性,不过,Node 只能用于链表的情况,红黑树的情况需要使用 TreeNode。

我们根据数组元素中,第一个节点数据类型是 Node 还是 TreeNode 来判断该位置下是链表还是红黑树的。

put过程

和jdk7的put差不多

get 过程分析

Java7 中实现的 ConcurrentHashMap 还是比较复杂的,Java8 对 ConcurrentHashMap 进行了比较大的改动。可以参考 Java8 中 HashMap 相对于 Java7 HashMap 的改动,对于 ConcurrentHashMap,Java8 也引入了红黑树。

在1.8版本以前,ConcurrentHashMap采用分段锁的概念,使锁更加细化,但是1.8已经改变了这种思路,而是利用CAS+Synchronized来保证并发更新的安全,底层采用数组+链表+红黑树的存储结构。

ConcurrentHashMap通常只被看做并发效率更高的Map,用来替换其他线程安全的Map容器,比如Hashtable和Collections.synchronizedMap。线程安全的容器,特别是Map,很多情况下一个业务中涉及容器的操作有多个,即复合操作,而在并发执行时,线程安全的容器只能保证自身的数据不被破坏,和数据在多个线程间是可见的,但无法保证业务的行为是否正确。

ConcurrentHashMap总结:

案例2:业务操作的线程安全不能保证

案例3:多线程删除

12.7 对比Hashtable
Hashtable和ConcurrentHashMap的不同点:

Hashtable对get,put,remove都使用了同步操作,它的同步级别是正对Hashtable来进行同步的,也就是说如果有线程正在遍历集合,其他的线程就暂时不能使用该集合了,这样无疑就很容易对性能和吞吐量造成影响,从而形成单点。而ConcurrentHashMap则不同,它只对put,remove操作使用了同步操作,get操作并不影响。
Hashtable在遍历的时候,如果其他线程,包括本线程对Hashtable进行了put,remove等更新操作的话,就会抛出ConcurrentModificationException异常,但如果使用ConcurrentHashMap的话,就不用考虑这方面的问题了



  • hashmap鏄嚎绋嬪畨鍏ㄧ殑鍚
    绛旓細ConcurrentHashMap 鏄 Java 闆嗗悎妗嗘灦鐨勪竴閮ㄥ垎锛屽畠鎻愪緵浜嗙嚎绋嬪畨鍏ㄧ殑 HashMap銆侰oncurrentHashMap 閫氳繃浣跨敤鍒嗘閿佹妧鏈紝鍏佽澶氫釜绾跨▼鍦ㄤ笉鍚岀殑娈典笂杩涜璇诲啓鎿嶄綔锛屼粠鑰屾彁楂樹簡骞跺彂鎬ц兘銆傛澶栵紝Java 8 寮曞叆鐨 ConcurrentHashMap.computeIfAbsent 鍜 ConcurrentHashMap.computeIfPresent 鏂规硶涔熶负骞跺彂璁$畻鎻愪緵浜嗘洿渚挎嵎鐨...
  • HashMap銆ConcurrentHashMap銆丠ashTable鐨勫尯鍒
    绛旓細寮曞叆 ConcurrentHashMap 鏄负浜嗗湪鍚屾闆嗗悎HashTable涔嬮棿鏈夋洿濂界殑閫夋嫨锛 HashTable 涓 HashMap 銆 ConcurrentHashMap 涓昏鐨勫尯鍒湪浜嶩ashMap涓嶆槸鍚屾鐨勩佺嚎绋嬩笉瀹夊叏鐨勫拰涓嶉傚悎搴旂敤浜庡绾跨▼骞跺彂鐜涓嬶紝鑰 ConcurrentHashMap 鏄嚎绋嬪畨鍏ㄧ殑闆嗗悎瀹瑰櫒锛岀壒鍒槸鍦ㄥ绾跨▼鍜屽苟鍙戠幆澧冧腑锛岄氬父浣滀负 Map 鐨勪富瑕佸疄鐜般傞櫎浜...
  • ConcurrentHashMap
    绛旓細鏁翠釜 ConcurrentHashMap 鐢变竴涓釜 Segment 缁勬垚锛孲egment 浠h〃鈥濋儴鍒嗏滄垨鈥濅竴娈碘滅殑鎰忔濓紝鎵浠ュ緢澶氫汉閮戒細灏嗗叾鎻忚堪涓哄垎娈甸攣銆傜畝鍗曠殑璇达紝ConcurrentHashMap 鏄竴涓 Segment 鏁扮粍锛孲egment 閫氳繃缁ф壙 ReentrantLock 鏉ヨ繘琛屽姞閿侊紝鎵浠ユ瘡娆¢渶瑕佸姞閿佺殑鎿嶄綔閿佷綇鐨勬槸涓涓 segment锛岃繖鏍峰彧瑕佷繚璇佹瘡涓 Segment 鏄嚎绋嬪畨鍏...
  • HashMap銆丠ashTable銆ConcurrentHashMap鐨勫師鐞嗕笌鍖哄埆
    绛旓細ConcurrentHashMap鏄敱Segment鏁扮粍缁撴瀯鍜孒ashEntry鏁扮粍缁撴瀯缁勬垚銆係egment鏄竴涓彲閲嶅叆閿侊紙ReentrantLock锛夛紝鍦–oncurrentHashMap閲屾壆婕旈攣鐨勮鑹诧紱HashEntry鍒欑敤浜庡瓨鍌ㄩ敭鍊煎鏁版嵁銆備竴涓狢oncurrentHashMap閲屽寘鍚竴涓猄egment鏁扮粍銆係egment鐨勭粨鏋勫拰HashMap绫讳技锛屾槸涓绉嶆暟缁勫拰閾捐〃缁撴瀯銆備竴涓猄egment閲屽寘鍚竴涓狧ashEntry鏁扮粍锛...
  • Java骞跺彂鍖呴噷鐨concurrentHashMap鍦ㄤ粈涔堟儏鍐典笅tryPresize鏂规硶閲岀殑s...
    绛旓細鍦 Java 鐨 ConcurrentHashMap 绫讳腑锛宼ryPresize 鏂规硶涓昏鐢ㄤ簬鍦ㄩ璋冩暣杩囩▼涓垽鏂槸鍚﹂渶瑕佽皟鏁 ConcurrentHashMap 鐨勫ぇ灏忋傝繖涓柟娉曠殑鍙傛暟 sc 鏄 ConcurrentHashMap 鐨 segment count锛堟鏁帮級銆傚湪 tryPresize 鏂规硶涓紝while 寰幆鐨勬潯浠舵槸 (sc > 1 && sc > ((sc >> 2) + 1))銆傝繖涓潯浠剁殑鎰忔濇槸锛...
  • 涓鍥句簡瑙ConcurrentHashMap搴曞眰鍘熺悊
    绛旓細1銆ConcurrentHashMap搴曞眰鏁版嵁缁撴瀯鏄竴涓暟缁則able 2銆乼able鏁扮粍涓婃寕鐫鍗曞悜閾捐〃鎴栫孩榛戞爲 3銆乶ew ConcurrentHashMap();濡傛灉娌℃湁鎸囧畾闀垮害鐨勮瘽锛岄粯璁ゆ槸16锛屽苟涓旀暟缁勯暱搴﹀繀椤绘槸2鐨刵娆″箓锛岃嫢鑷畾涔夊垵濮嬪寲鐨勯暱搴︿笉鏄2鐨刵娆″箓锛岄偅涔堝湪鍒濆鍖栨暟缁勬椂锛屼細鍚ф暟缁勯暱搴﹁缃负澶т簬鑷畾涔夐暱搴︾殑鏈杩戠殑2鐨刵娆″箓銆傦紙濡傦細...
  • hashmap鍜concurrenthashmap鐨勫尯鍒槸浠涔?
    绛旓細hashmap鍜concurrenthashmap鐨勫尯鍒涓嬶細HashMap涓嶆槸绾跨▼瀹夊叏鐨勶紝鑰ConcurrentHashMap鏄嚎绋嬪畨鍏ㄧ殑銆侰oncurrentHashMap閲囩敤閿佸垎娈垫妧鏈紝灏嗘暣涓狧ash妗惰繘琛屼簡鍒嗘segment锛屼篃灏辨槸灏嗚繖涓ぇ鐨勬暟缁勫垎鎴愪簡鍑犱釜灏忕殑鐗囨segment锛岃屼笖姣忎釜灏忕殑鐗囨segment涓婇潰閮芥湁閿佸瓨鍦ㄣ傞偅涔堝湪鎻掑叆鍏冪礌鐨勬椂鍊欏氨闇瑕佸厛鎵惧埌搴旇鎻掑叆鍒板摢涓涓...
  • 濡備綍鍦╦ava涓娇鐢ConcurrentHashMap
    绛旓細鍙傝冨涓嬪唴瀹:ConcurrentHashMap閿佺殑鏂瑰紡鏄◢寰粏绮掑害鐨勩 ConcurrentHashMap灏唄ash琛ㄥ垎涓16涓《锛堥粯璁ゅ硷級锛岃濡俫et,put,remove绛夊父鐢ㄦ搷浣滃彧閿佸綋鍓嶉渶瑕佺敤鍒扮殑妗躲傝瘯鎯筹紝鍘熸潵 鍙兘涓涓嚎绋嬭繘鍏ワ紝鐜板湪鍗磋兘鍚屾椂16涓啓绾跨▼杩涘叆锛堝啓绾跨▼鎵嶉渶瑕侀攣瀹氾紝鑰岃绾跨▼鍑犱箮涓嶅彈闄愬埗锛屼箣鍚庝細鎻愬埌锛夛紝骞跺彂鎬х殑鎻愬崌鏄樉鑰...
  • hashmap鍜concurrenthashmap鐨勫尯鍒槸浠涔?
    绛旓細鏈澶х殑鍖哄埆灏辨槸ConcurrentHashMap鏄嚎绋嬪畨鍏ㄧ殑,hashMap涓嶆槸绾跨▼瀹夊叏鐨勩傚熀浜庡搱甯岃〃鐨 Map 鎺ュ彛鐨勫疄鐜般傛瀹炵幇鎻愪緵鎵鏈夊彲閫夌殑鏄犲皠鎿嶄綔锛屽苟鍏佽浣跨敤 null 鍊煎拰 null 閿傦紙闄や簡闈炲悓姝ュ拰鍏佽浣跨敤 null 涔嬪锛孒ashMap 绫讳笌 Hashtable 澶ц嚧鐩稿悓銆傦級姝ょ被涓嶄繚璇佹槧灏勭殑椤哄簭锛岀壒鍒槸瀹冧笉淇濊瘉璇ラ『搴忔亽涔呬笉鍙樸傛...
  • ConcurrentHashMap闈㈣瘯闂鎬荤粨
    绛旓細1.ConcurrentHashMap鏄嚎绋嬪畨鍏ㄧ殑锛孒ashMap鏄嚎绋嬩笉瀹夊叏鐨 2.ConcurrentHashMap涓嶅厑璁窴ey涓簄ull鐨勫兼彃鍏ャ傝孒ashMap鏄彲浠ョ殑 Q:JDK8鐨凜oncurrentHashMap鍜孞DK7鐨凜oncurrentHashMap鏈変粈涔堝尯鍒紵A:1.JDK7鐨凜oncurrentHashMap閲囩敤鍒嗘閿佺殑绛栫暐锛屽皢鏁翠釜鏁扮粍鍒嗘垚澶氫釜segment锛屽杩欎簺segment杩涜鍒嗘鍔犻攣锛屼娇鐢ㄧ殑閿佹槸...
  • 本站交流只代表网友个人观点,与本站立场无关
    欢迎反馈与建议,请联系电邮
    2024© 车视网