ConcurrentHashMap之实现细节2
数据结构
关于Hash表的基础数据结构,这里不想做过多的探讨。Hash表的一个很重要方面就是如何解决hash冲突,ConcurrentHashMap 和HashMap使用相同的方式,都是将hash值相同的节点放在一个hash链中。与HashMap不同的是,ConcurrentHashMap使用 多个子Hash表,也就是段(Segment)。下面是ConcurrentHashMap的数据成员:
Java代码 <embed height="15" width="14" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" allowscriptaccess="always" quality="high" flashvars="clipboard=public%20class%20ConcurrentHashMap%3CK%2C%20V%3E%20extends%20AbstractMap%3CK%2C%20V%3E%0A%20%20%20%20%20%20%20%20implements%20ConcurrentMap%3CK%2C%20V%3E%2C%20Serializable%20%7B%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20Mask%20value%20for%20indexing%20into%20segments.%20The%20upper%20bits%20of%20a%0A%20%20%20%20%20*%20key's%20hash%20code%20are%20used%20to%20choose%20the%20segment.%0A%20%20%20%20%20*%2F%0A%20%20%20%20final%20int%20segmentMask%3B%0A%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20Shift%20value%20for%20indexing%20within%20segments.%0A%20%20%20%20%20*%2F%0A%20%20%20%20final%20int%20segmentShift%3B%0A%0A%20%20%20%20%2F**%0A%20%20%20%20%20*%20The%20segments%2C%20each%20of%20which%20is%20a%20specialized%20hash%20table%0A%20%20%20%20%20*%2F%0A%20%20%20%20final%20Segment%3CK%2CV%3E%5B%5D%20segments%3B%0A%7D" src="http://www.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" lk_mediaid="lk_juiceapp_mediaPopup_1236652704279" lk_media="yes">
- public class ConcurrentHashMap<K, V> extends AbstractMap<K, V>
- implements ConcurrentMap<K, V>, Serializable {
- /**
- * Mask value for indexing into segments. The upper bits of a
- * key's hash code are used to choose the segment.
- */
- final int segmentMask;
- /**
- * Shift value for indexing within segments.
- */
- final int segmentShift;
- /**
- * The segments, each of which is a specialized hash table
- */
- final Segment<K,V>[] segments;
- }
所有的成员都是final的,其中segmentMask和segmentShift主要是为了定位段,参见上面的segmentFor方法。
每个Segment相当于一个子Hash表,它的数据成员如下:
Java代码 <embed height="15" width="14" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" allowscriptaccess="always" quality="high" flashvars="clipboard=%20%20%20%20static%20final%20class%20Segment%3CK%2CV%3E%20extends%20ReentrantLock%20implements%20Serializable%20%7B%0Aprivate%20static%20final%20long%20serialVersionUID%20%3D%202249069246763182397L%3B%0A%20%20%20%20%20%20%20%20%2F**%0A%20%20%20%20%20%20%20%20%20*%20The%20number%20of%20elements%20in%20this%20segment's%20region.%0A%20%20%20%20%20%20%20%20%20*%2F%0A%20%20%20%20%20%20%20%20transient%20volatile%20int%20count%3B%0A%0A%20%20%20%20%20%20%20%20%2F**%0A%20%20%20%20%20%20%20%20%20*%20Number%20of%20updates%20that%20alter%20the%20size%20of%20the%20table.%20This%20is%0A%20%20%20%20%20%20%20%20%20*%20used%20during%20bulk-read%20methods%20to%20make%20sure%20they%20see%20a%0A%20%20%20%20%20%20%20%20%20*%20consistent%20snapshot%3A%20If%20modCounts%20change%20during%20a%20traversal%0A%20%20%20%20%20%20%20%20%20*%20of%20segments%20computing%20size%20or%20checking%20containsValue%2C%20then%0A%20%20%20%20%20%20%20%20%20*%20we%20might%20have%20an%20inconsistent%20view%20of%20state%20so%20(usually)%0A%20%20%20%20%20%20%20%20%20*%20must%20retry.%0A%20%20%20%20%20%20%20%20%20*%2F%0A%20%20%20%20%20%20%20%20transient%20int%20modCount%3B%0A%0A%20%20%20%20%20%20%20%20%2F**%0A%20%20%20%20%20%20%20%20%20*%20The%20table%20is%20rehashed%20when%20its%20size%20exceeds%20this%20threshold.%0A%20%20%20%20%20%20%20%20%20*%20(The%20value%20of%20this%20field%20is%20always%20%3Ctt%3E(int)(capacity%20*%0A%20%20%20%20%20%20%20%20%20*%20loadFactor)%3C%2Ftt%3E.)%0A%20%20%20%20%20%20%20%20%20*%2F%0A%20%20%20%20%20%20%20%20transient%20int%20threshold%3B%0A%0A%20%20%20%20%20%20%20%20%2F**%0A%20%20%20%20%20%20%20%20%20*%20The%20per-segment%20table.%0A%20%20%20%20%20%20%20%20%20*%2F%0A%20%20%20%20%20%20%20%20transient%20volatile%20HashEntry%3CK%2CV%3E%5B%5D%20table%3B%0A%0A%20%20%20%20%20%20%20%20%2F**%0A%20%20%20%20%20%20%20%20%20*%20The%20load%20factor%20for%20the%20hash%20table.%20%20Even%20though%20this%20value%0A%20%20%20%20%20%20%20%20%20*%20is%20same%20for%20all%20segments%2C%20it%20is%20replicated%20to%20avoid%20needing%0A%20%20%20%20%20%20%20%20%20*%20links%20to%20outer%20object.%0A%20%20%20%20%20%20%20%20%20*%20%40serial%0A%20%20%20%20%20%20%20%20%20*%2F%0A%20%20%20%20%20%20%20%20final%20float%20loadFactor%3B%0A%7D" src="http://www.javaeye.com/javascripts/syntaxhighlighter/clipboard_new.swf" lk_mediaid="lk_juiceapp_mediaPopup_1236652704286" lk_media="yes">
- static final class Segment<K,V> extends ReentrantLock implements Serializable {
- private static final long serialVersionUID = 2249069246763182397L;
- /**
- * The number of elements in this segment's region.
- */
- transient volatile int count;
- /**
- * Number of updates that alter the size of the table. This is
- * used during bulk-read methods to make sure they see a
- * consistent snapshot: If modCounts change during a traversal
- * of segments computing size or checking containsValue, then
- * we might have an inconsistent view of state so (usually)
- * must retry.
- */
- transient int modCount;
- /**
- * The table is rehashed when its size exceeds this threshold.
- * (The value of this field is always <tt>(int)(capacity *
- * loadFactor)</tt>.)
- */
- transient int threshold;
- /**
- * The per-segment table.
- */
- transient volatile HashEntry<K,V>[] table;
- /**
- * The load factor for the hash table. Even though this value
- * is same for all segments, it is replicated to avoid needing
- * links to outer object.
- * @serial
- */
- final float loadFactor;
- }
count用来统计该段数据的个数,它是volatile,它用来协调修改和读取操作,以保证读取操作能够读取到几乎最新的修改。协调方式是这样的,每次 修改操作做了结构上的改变,如增加/删除节点(修改节点的值不算结构上的改变),都要写count值,每次读取操作开始都要读取count的值。这利用了 Java 5中对volatile语义的增强,对同一个volatile变量的写和读存在happens-before关系。modCount统计段结构改变的次 数,主要是为了检测对多个段进行遍历过程中某个段是否发生改变,在讲述跨段操作时会还会详述。threashold用来表示需要进行rehash的界限 值。table数组存储段中节点,每个数组元素是个hash链,用HashEntry表示。table也是volatile,这使得能够读取到最新的 table值而不需要同步。loadFactor表示负载因子。