ConcurrentHashMap是线程安全,性能出色的Map的线程安全实现,相比较HashMap他是线程安全的,相比较HashTable他的性能优点很是明显。他的使用很简单,这里主要是想要探究一下ConcurrentHashMap的实现原理。
在这里一共有 个问题须要搞明白。java
带着这几个问题咱们来分析一下ConcurrentHashMap的源码吧。算法
在JDK8(JDK7也是同样)中ConcurrentHashMap的定义以下:数组
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V> implements ConcurrentMap<K,V>, Serializable {
Java7中ConcurrentHashMap的实现是基于分段锁实现的。他的底层数据结构仍然是数组+链表,与HashTable不一样的是,ConcurrentHashMap的最外层不是一个大的数组,而是一个Segment数组(分段锁的实现)。分段锁减少了锁的粒度,提升了并发程度。这也是为何比HashTable效率要高的缘由。
HashTable的源码其实很简单,HashTable和HashMap的结构一致,可是每个方法都是用Synchronized来修饰,以保证操做是线程安全的。这样在多线程的状况下,只有一个线程获取锁操做hashTable中的数据。而CourrentHashMap则不是,它容许最多有segment数组长度个线程同时操做ConcurrentHashMap中的数据。安全
ConcurrentHashMap的总体结构以下(图片来源:http://www.jasongj.com/java/c...):数据结构
ConcurrentHashMap的定义:多线程
public class ConcurrentHashMap<K, V> extends AbstractMap<K, V> implements ConcurrentMap<K, V>, Serializable { private static final long serialVersionUID = 7249069246763182397L; /** * 表的默认容量 */ static final int DEFAULT_INITIAL_CAPACITY = 16; /** * 默认扩容因子 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * segments数组的默认长度,为了能经过按位与的散列算法来定位segments数组的索引,必须保证segments数组的长度是2的N次方 */ static final int DEFAULT_CONCURRENCY_LEVEL = 16; /** * HashEntry最大容量 */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * segment的最小容量 */ static final int MIN_SEGMENT_TABLE_CAPACITY = 2; /** * segment的最大容量 */ static final int MAX_SEGMENTS = 1 << 16; // slightly conservative /** * 重试次数,无锁的状况下尝试两次 */ static final int RETRIES_BEFORE_LOCK = 2; /** * 散列运算的掩码,等于ssize-1 */ final int segmentMask; /** * 定位参与散列运算的位数,等于32-sshift */ final int segmentShift; /** * 定义segment数组 */ final Segment<K,V>[] segments;
Segment定义:并发
static final class Segment<K,V> extends ReentrantLock implements Serializable { transient volatile HashEntry<K,V>[] table; transient int count; transient int modCount; //扩容量,默认为表的容量*加载因子,实际量超过这个值的时候,进行扩容 transient int threshold; //segment中hashEntry的扩容因子 final float loadFactor; }