ConcurrentHashMap详解
HashTable和Vector是线程安全的(本质是Synchronized),其他的集合(ArrayList…)是通过Collections.synchronized*()系列的方法来实现线程安全操作,JDK1.5后提供了线程安全的容器,在Java.util.concurrent包路径下
通过继承图可知,线程安全的类在ConcurrentMap接口下,该接口有两个实现类:ConcurrentHashMap和ConcurrentSkipListMap
以上四个方法都是线程安全的操作
- ConcurrentHashMap
属性及默认值
//默认初始容量 16
static final int DEFAULT_INITIAL_CAPACITY = 16;
//默认加载因子 0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//默认的并发度 16 ->segments数组的大小
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
//容量的上限->segments数组的大小
static final int MAXIMUM_CAPACITY = 1 << 30;
//Segment中HashEntry数组的最小值
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;
//主要作用于keyhash过程
final int segmentMask;
final int segmentShift;
//存储数据节点的位置。Segment的数组
final Segment<K,V>[] segments;
//获取key对应集合
transient Set<K> keySet;
//获取key-value键值对集合
transient Set<Map.Entry<K,V>> entrySet;
//获取value的集合
transient Collection<V> values;
//segment的锁是ReentantLock锁的子类
static final class Segment<K,V> extends ReentrantLock implements Serializable {
static final int MAX_SCAN_RETRIES =
Runtime.getRuntime().availableProcessors() > 1 ? 64 : 1;
//存放数据
transient volatile HashEntry<K,V>[] table;
//存放数据的个数
transient int count;
transient int modCount;
transient int threshold;
final float loadFactor;
}
//HashEntry的结构
static final class HashEntry<K,V> {
final int hash;
final K key;
volatile V value;
volatile HashEntry<K,V> next;
}
默认情况下ConcurrentHashMap用了16个Segment的结构(HashEntry的数组),每一个Segment又拥有一个独占锁,对同一个segment下的操作存在互斥(写是护指的,读存在并发),不同的segment之间是相互独立的,不存在并发的问题,也就是:ConcurrentHashMap下同一时刻至少可以是16个线程(默认并发度)进行并发操作
- 构造函数
//通过初试容量、加载因子、并发度来创建ConcurrentHashMap实例
public ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLevel) {
//对加载因子,初始容量,并发读的最小值做限制
if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
//并发读最大值做线程
if (concurrencyLevel > MAX_SEGMENTS)
concurrencyLevel = MAX_SEGMENTS;
// Find power-of-two sizes best matching arguments
//记录ssize的左移的次数
int sshift = 0;
//ssize的值是有concurrencyLevel类决定的,大小为大于concurrencyLevel的最小的2的N次方
int ssize = 1;
//concurrencyLevel=17,
// 1< 17 sshfit =1, ssize=2;
//2<17 sshfit=2,ssize=4
//4<17 sshfit=3,ssize = 8
//8<17 sshfit=4,ssize= 16
//16<17 shhfit=5,ssize=32;
//32<17 break ssize = 32 ,sshfit=5
while (ssize < concurrencyLevel) {
++sshift;
ssize <<= 1;
}
//段偏移量
this.segmentShift = 32 - sshift;
//散列算法的掩码,默认情况segmentMask=15
this.segmentMask = ssize - 1; //1111111->key的hash需要segmentMask
//容量上限
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//c记录每个segment上要记录的元素
int c = initialCapacity / ssize;
//加入有余数,则Segment进行+1
if (c * ssize < initialCapacity)
++c;
int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c) //保证HashEntry数组是2的倍数
cap <<= 1;
// create segments and segments[0]
Segment<K,V> s0 =new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
(HashEntry<K,V>[])new HashEntry[cap]);
//创建ssize长度的Segment的数组,默认初始情况下是16
Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];
//将所有的Segment进行初始化
UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
this.segments = ss;
}
public ConcurrentHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);
}
public ConcurrentHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
public ConcurrentHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
}
//通过集合来创建ConcurrentHashMap的实例
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY),
DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);
putAll(m);
}
本人才疏学浅,如有错误,烦请指出,谢谢!