您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > Java并发编程与技术内幕
Java并发编程与技术内幕:ConcurrentHashMap源码解析ConcurrentHashMap是Java并发包中非常有用的一个类,在高并发场景用得非常多,它是线程安全的。要注意到虽然HashTable虽然也是线程安全的,但是它的性能在高并发场景下完全比不上ConcurrentHashMap,这也是由它们的结构所决定的,可以认为ConcurrentHashMap是HashTable的加强版,不过这加强版和原来的HashTable有非常大的区别,不仅是在结构上,而且在方法上也有差别。下面先来简单说说它们的区别吧HashMap、HashTable、ConcurrentHashMap的区别吧1、HashMap是非线程安全,HashTable、ConcurrentHashMap都是线程安全,而且ConcurrentHashMap、Hashtable不能传入nul的key或value,HashMap可以。2、Hashtable是将数据放入到一个Entrty数组或者它Entrty数组上一个Entrty的链表节点。而ConcurrentHashMap是由Segment数组组成,每一个Segment可以看成是一个单独的Map.然后每个Segment里又有一个HashEntrty数组用来存放数据。3、HashTable的get/put/remove方法都是基于同步的synchronized方法,而ConcurrentHashMap是基本锁的机制,并且每次不是锁全表,而是锁单独的一个Segment。所以ConcurrentHashMap的性能比HashTable好。4、如果不考虑线程安全因素,推荐使用HashMap,因为它性能最好。首先来看看它包含的结构吧![java]viewplaincopy在CODE上查看代码片派生到我的代码片publicclassConcurrentHashMapK,VextendsAbstractMapK,VimplementsConcurrentMapK,V,Serializable{privatestaticfinallongserialVersionUID=7249069246763182397L;//默认数组容量大小staticfinalintDEFAULT_INITIAL_CAPACITY=16;//默认装载因子staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;//默认层级staticfinalintDEFAULT_CONCURRENCY_LEVEL=16;//最大的每一个数组容量大小staticfinalintMAXIMUM_CAPACITY=130;//最大的分组数目staticfinalintMAX_SEGMENTS=116;//slightlyconservative//调用remove/contain/replace方法时不加锁的情况下操作重试次数staticfinalintRETRIES_BEFORE_LOCK=2;//segments数组索引相关finalintsegmentMask;//segments数组偏移相关finalintsegmentShift;//segments数组,每个segments单独就可以认为是一个mapfinalSegmentK,V[]segments;这里看到了一个SegmentK,V[]segments;数组。下面再来看看Segment这个类。在下面可以看到Segment这个类继承了ReentrantLock锁。所以它也是一个锁。然后它里面还有一个HashEntryK,V[]table。这是真正用来存放数据的结构。[java]viewplaincopy在CODE上查看代码片派生到我的代码片/***Segment内部类,注意它也是一个锁!可以认为它是一个带有锁方法的map*/staticfinalclassSegmentK,VextendsReentrantLockimplementsSerializable{privatestaticfinallongserialVersionUID=2249069246763182397L;//元素个数transientvolatileintcount;//修改次数transientintmodCount;//阈值,超过这个数会重新reSizetransientintthreshold;//注意,这里又一个数组,这个是真正存放数据元素的地方transientvolatileHashEntryK,V[]table;//装载因子,用来计算thresholdfinalfloatloadFactor;HashEntry它的结构很简单:[java]viewplaincopy在CODE上查看代码片派生到我的代码片staticfinalclassHashEntryK,V{finalKkey;finalinthash;//用来保存Segment索引的信息volatileVvalue;finalHashEntryK,Vnext;HashEntry(Kkey,inthash,HashEntryK,Vnext,Vvalue){this.key=key;this.hash=hash;this.next=next;this.value=value;}@SuppressWarnings(unchecked)staticfinalK,VHashEntryK,V[]newArray(inti){returnnewHashEntry[i];}}经过上面的分析:可以得出如下的ConcurrentHashMap结构图全部源码分析:[java]viewplaincopy在CODE上查看代码片派生到我的代码片packagejava.util.concurrent;importjava.util.concurrent.locks.*;importjava.util.*;importjava.io.Serializable;importjava.io.IOException;importjava.io.ObjectInputStream;importjava.io.ObjectOutputStream;publicclassConcurrentHashMapK,VextendsAbstractMapK,VimplementsConcurrentMapK,V,Serializable{privatestaticfinallongserialVersionUID=7249069246763182397L;//默认数组容量大小staticfinalintDEFAULT_INITIAL_CAPACITY=16;//默认装载因子staticfinalfloatDEFAULT_LOAD_FACTOR=0.75f;//默认层级staticfinalintDEFAULT_CONCURRENCY_LEVEL=16;//最大的每一个数组容量大小staticfinalintMAXIMUM_CAPACITY=130;//最大的分组数目staticfinalintMAX_SEGMENTS=116;//slightlyconservative//调用remove/contain/replace方法时不加锁的情况下操作重试次数staticfinalintRETRIES_BEFORE_LOCK=2;//segments数组索引相关finalintsegmentMask;//segments数组偏移相关finalintsegmentShift;//segments数组,每个segments单独就可以认为是一个mapfinalSegmentK,V[]segments;/***哈希算法*/privatestaticinthash(inth){//Spreadbitstoregularizebothsegmentandindexlocations,//usingvariantofsingle-wordWang/Jenkinshash.h+=(h15)^0xffffcd7d;h^=(h10);h+=(h3);h^=(h6);h+=(h2)+(h14);returnh^(h16);}/***根据哈希值计算应该落在哪个segments上*/finalSegmentK,VsegmentFor(inthash){returnsegments[(hashsegmentShift)&segmentMask];}/***内部类,每个HashEntry都会存入到一个Segment中去*/staticfinalclassHashEntryK,V{finalKkey;//关键字finalinthash;//哈希值volatileVvalue;//值finalHashEntryK,Vnext;//不同的关键字,相再的哈希值时会组成一个链表HashEntry(Kkey,inthash,HashEntryK,Vnext,Vvalue){this.key=key;this.hash=hash;this.next=next;this.value=value;}@SuppressWarnings(unchecked)staticfinalK,VHashEntryK,V[]newArray(inti){returnnewHashEntry[i];}}/***Segment内部类,注意它也是一个锁!可以认为它是一个带有锁方法的map*/staticfinalclassSegmentK,VextendsReentrantLockimplementsSerializable{privatestaticfinallongserialVersionUID=2249069246763182397L;//元素个数transientvolatileintcount;//修改次数transientintmodCount;//阈值,超过这个数会重新reSizetransientintthreshold;//注意,这里又一个数组,这个是真正存放数据元素的地方transientvolatileHashEntryK,V[]table;//装载因子,用来计算thresholdfinalfloatloadFactor;//构造函数,由initialCapacity确定table的大小Segment(intinitialCapacity,floatlf){loadFactor=lf;setTable(HashEntry.K,VnewArray(initialCapacity));}@SuppressWarnings(unchecked)staticfinalK,VSegmentK,V[]newArray(inti){returnnewSegment[i];}//设置threshold、tablevoidsetTable(HashEntryK,V[]newTable){threshold=(int)(newTable.length*loadFactor);//注意,当table的元素个数超过这个时,会触发reSize;table=newTable;}//取得头一个HashEntryK,VgetFirst(inthash){HashEntryK,V[]tab=table;returntab[hash&(tab.length-1)];}//在加锁情况下读数据,注意这个类继续了锁的方法VreadValueUnderLock(HashEntryK,Ve){lock();try{returne.value;}finally{unlock();}}//取元素Vget(Objectkey,inthash){if(count!=0){//注意,没有加锁HashE
本文标题:Java并发编程与技术内幕
链接地址:https://www.777doc.com/doc-2880993 .html