ConcurrentHashMap是HashMap在多线程下使用方式,采用分段锁原理。在JDK7和JDK8中实现原理不同, JDK7中使用标准分段锁,默认为16段,可设置,一旦设置不可修改,锁粒度大,通过ReentrantLock实现锁。JDK8中通过使用CAS和synchronize关键字实现,本文我们通过源码来解析ConcurrentHashMap的原理。
数据结构
ConcurrentHashMapd的组成是有数组+链表+红黑树实现的,JDK8中HashMap和ConcurrentHashMap结构类似:
- 根据key取hash值,然后按位与运算,找到数组的位置
- 如果对应位置没有元素,则直接放入数组中;
- 如果有元素,并且是链表,加入链表尾部;
- 如果是红黑树,则存入红黑树,并做平衡处理
转成红黑树的目的就是为了解决链表长度过大, 每次遍历查询时间过长, 提升了效率
几个重要参数
// 默认初始化容量
private static final int DEFAULT_CAPACITY = 16;
// 加载因子 当Map中元素个数超过容量的0.75倍后,开始扩容
private static final float LOAD_FACTOR = 0.75f;
// 链表元素超过8个后转为红黑树
static final int TREEIFY_THRESHOLD = 8;
// 链表元素个数低于6个后转为链表
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
Node为数组中存放的元素,结构如下
static class Node<K,V> implements Map.Entry<K,V> {
final inthash;
final K key;
volatile V val;
volatile Node<K,V> next;
...
public final int hashCode() { return key.hashCode() ^ val.hashCode(); }
public final V setValue(V value) {
throw new UnsupportedOperationException();
}
public final boolean equals(Object o) {
Object k, v, u; Map.Entry<?,?> e;
return ((o instanceof Map.Entry) &&
(k = (e = (Map.Entry<?,?>)o).getKey()) != null &&
(v = e.getValue()) != null &&
(k == key || k.equals(key)) &&
(v == (u = val) || v.equals(u)));
}
/**
* Virtualized support for map.get(); overridden in subclasses.
* 红黑树查找元素
*/
Node<K,V> find(int h, Object k) {
Node<K,V> e = this;
if (k != null) {
do {
K ek;
if (e.hash == h &&
((ek = e.key) == k || (ek != null && k.equals(ek))))
return e;
} while ((e = e.next) != null);
}
return null;
}
}
构造方法
public ConcurrentHashMap(int initialCapacity) {
if(initialCapacity < 0)
throw new IllegalArgumentException();
// 初始化容量会设置为与initialCapacity的2倍相近的2的次幂的值, 如果前后相等, 取后值
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
MAXIMUM_CAPACITY :
tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
this.sizeCtl = cap;
}
Hash
使用key自己的hash值, 然后spread转换: 使用hash值与高16异或运算后再和32位全是1做与运算, 目的保留高位特性, 降低hash碰撞概率
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}
get获取元素
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode()); // 根据hash值计算元素所在数组位置
// table 不是空 所在位置也不是空
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) { // 当前元素为所查找元素
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0) // 表示为红黑树
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) { // 链表查询
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
put添加元素
final V putVal(K key, V value, boolean onlyIfAbsent) { // onlyIfAbsent 仅在不存在时存入元素, 如果存在返回原值
// 元素kv都不能为空
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode()); // 计算hash
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable(); // 初始化map
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { // 元素应在数组位置为空
// 使用CAS更换元素
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED) // map正在发生扩容
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) { // 对元素应在数组位置元素加锁
if (tabAt(tab, i) == f) { // 再次校验
if (fh >= 0) { // 链表结构
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) { // 元素已存在
oldVal = e.val; // 保存旧值
if (!onlyIfAbsent) // 不管是否存在都覆盖
e.val = value;
break;
}
// 未找到元素 放入链表尾部
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
else if (f instanceof TreeBin) { // 红黑树结构
Node<K,V> p;
binCount = 2;
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
oldVal = p.val;
if (!onlyIfAbsent) // 不管是否存在都覆盖
p.val = value;
}
}
}
}
if (binCount != 0) {
// 如果元素不存在, 并且是链表, 遍历链表时binCount在增加, 如果大于8, 转为红黑树
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i); // 转为红黑树
if (oldVal != null) // 如果是覆盖旧元素, 返回旧元素
return oldVal;
break;
}
}
}
addCount(1L, binCount); // 检查是否需要扩容
return null;
}