JDK源码之ConcurrentHashMap类分析
一 概述
jdk1.8之前
在JDK1.6中,ConcurrentHashMap将数据分成一段一段存储,给每一段数据配一把锁,当一个线程获得锁互斥访问一个段数据时,其他段的数据也可被其他线程访问;
每个Segment拥有一把可重入锁,因此ConcurrentHashMap的分段锁数目即为Segment数组长度。
ConcurrentHashMap结构:每一个segment都是一个HashEntry<K,V>[] table, table中的每一个元素本质上都是一个HashEntry的单向队列(单向链表实现)。
每一个segment都是一个HashEntry<K,V>[] table, table中的每一个元素本质上都是一个HashEntry的单向队列
jdk1.8之后
在jdk1.8以上已经抛弃了Segment的概念,底层数据结构与HashMap相同,仍然采用table数组+链表+红黑树结构;
一个线程进行put/remove操作时,对桶(链表 or 红黑树)加上synchronized独占锁;ConcurrentHashMap采用CAS算法保证线程安全;
这里只讨论分析jdk1.8的源代码
二 ConcurrentMap接口
ConcurrentHashMap实现了ConcurrentMap接口,是一种提供线程安全性和原子性保证的Map.之前HashMap中已经分析过抽象map接口,这里简单分析下此接口:
抽象方法
// 这四个方法在Map<K,V>接口已经定义,并且都是default方法。
// 在ConcurrentMap中因为这些方法需要保证多线程下的原子性,
// 因此在ConcurrentMap中都是abstract方法,需要子类实现,保证线程安全。
V putIfAbsent(K key, V value);
boolean remove(Object key, Object value);
boolean replace(K key, V oldValue, V newValue);
V replace(K key, V value);
// 其他还有些覆写了Map接口的部分default方法,实现稍微会有点区别
三 源码分析
ConcurrentHashMap里面有大部分常量和底层结构方法都跟HashMap相同,这里不全分析,只重点分析跟HashMap不同的地方以及实现原理:
属性
// 默认并发线程数
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
// 每次进行转移的最小值
private static final int MIN_TRANSFER_STRIDE = 16;
// 生成sizeCtl所使用的bit位数
private static final int RESIZE_STAMP_BITS = 16;
// 进行扩容所允许的最大线程数
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
// 记录sizeCtl中的大小所需要进行的偏移位数
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
// 一系列的标识
static final int MOVED = -1; // hash for forwarding nodes
static final int TREEBIN = -2; // hash for roots of trees
static final int RESERVED = -3; // hash for transient reservations
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
// 获取可用的CPU个数
static final int NCPU = Runtime.getRuntime().availableProcessors();
//table数组
transient volatile Node<K,V>[] table;
// rehash扩容时用到的新键值对数组
private transient volatile Node<K,V>[] nextTable;
// 记录当前键值对总数,通过CAS更新,对所有线程可见
private transient volatile long baseCount;
/**
* sizeCtl表示键值对总数阈值,通过CAS更新, 对所有线程可见
* 当sizeCtl < 0时,表示多个线程在等待扩容;
* 当sizeCtl = 0时,默认值;
* 当sizeCtl > 0时,表示扩容的阈值;
*/
private transient volatile int sizeCtl;
// 扩容下一个表的索引
private transient volatile int transferIndex;
// 自旋锁
private transient volatile int cellsBusy;
// counterCell表
private transient volatile CounterCell[] counterCells;
// 视图
private transient KeySetView<K,V> keySet;
private transient ValuesView<K,V> values;
private transient EntrySetView<K,V> entrySet;
Node静态内部类
vaue值和next用volatile修饰,不支持setValue,提供find方法,其他和HashMap中Node一样:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
volatile V val; // val和next都使用volatile修饰
volatile Node<K,V> next;
// ..... 构造方法和get/setKey(),hashCode等方法
// 不支持setValue
public final V setValue(V value) {
throw new UnsupportedOperationException();
}
// 提供查找方法,调用节点只能是哈希桶链表头节点
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;
}
}