ConcurrentHashMap是java.util.concurrent并发包下的一个类,相比jdk1.7,jdk1.8使用了cas来替代分段锁,其和HashMap一样底层使用了数组+链表+红黑树。
字段
private static final int MAXIMUM_CAPACITY = 1 << 30;//最大容量 2^30
private static final int DEFAULT_CAPACITY = 16; //默认容量 2^4
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;//最大数组大小
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;//jdk1.8没有使用,为了和上个版本兼容
private static final float LOAD_FACTOR = 0.75f;//加载因子
static final int TREEIFY_THRESHOLD = 8; //树化阈值
static final int UNTREEIFY_THRESHOLD = 6; //树转链表阈值
static final int MIN_TREEIFY_CAPACITY = 64;//最小树化容量
private static final int MIN_TRANSFER_STRIDE = 16;
private static int RESIZE_STAMP_BITS = 16;//resizeStamp(n)生成戳的位数
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;//最大帮助扩容线程数
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
/**
* 当sizeCt=-1时表示正在初始化
* 当sizeCtl<-1时,表示扩容的线程数量为|sizeCtl|-1
* 当sizeCtl>=0时,表示下次扩容的阈值
*/
private transient volatile int sizeCtl;
在看putVal()方法之前,先来了解一下table的初始化方法
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
//确认其他线程未对table修改
while ((tab = table) == null || tab.length == 0) {
//如果有其他线程在进行初始化或者扩容,则让出线程
if ((sc = sizeCtl) < 0)
Thread.yield(); // lost initialization race; just spin
//否则cas修改sizeCtl为-1,表示正在进行初始化操作
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) {
try {
//再次确认table还未被其他线程修改(可能在cas竞争到锁后,其他线程已经对table完成修改)
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
//sc = 0.75 * n
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}
ConcurrentHashMap的put()方法和HashMap的实现思想都差不多,我们直接来看putVal()方法
final V putVal(K key, V value, boolean onlyIfAbsent) {
//如果key/value为空,抛出空指针异常 ConcurrentHashMap不允许键值对象为null
if (key == null || value == null) throw new NullPointerException();
//计算hash值
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//如果table没有进行初始化,则先进行初始化操作
if (tab == null || (n = tab.length) == 0)
tab = initTable();
//(n - 1) & hash) 相当于 hash%n即table的索引,tabAt()获取table的指定索引的节点
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
//如果节点为空,那么直接插入,casTabAt()将节点插入指定位置
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)
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;
//如果key相同,则更新值
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;
}
}
}
//ConcurrentHashMap 树节点为TreeBin,其封装了TreeNode
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) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
addCount(1L, binCount);
return null;
}
get()方法类似,
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
//计算hash
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
//如果key相同,则返回value
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
//如果小于0则为特殊节点,使用find方法寻找
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;
}
tryPreSize()方法和resizeStamp()我的一篇博客中讲过ConcurrentHashMap源码之tryPreSize
final V replaceNode(Object key, V value, Object cv) {
//计算hash
int hash = spread(key.hashCode());
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//表为空则返回
if (tab == null || (n = tab.length) == 0 ||
(f = tabAt(tab, i = (n - 1) & hash)) == null)
break;
//如果节点正在移动,则帮助其移动
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
boolean validated = false;
synchronized (f) {
//即检查table是否修改
if (tabAt(tab, i) == f) {
if (fh >= 0) {
validated = true;
for (Node<K,V> e = f, pred = null;;) {
K ek;
//找到key,然后修改指针,使其孤立
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
V ev = e.val;
if (cv == null || cv == ev ||
(ev != null && cv.equals(ev))) {
oldVal = ev;
if (value != null)
e.val = value;
else if (pred != null)
pred.next = e.next;
else
setTabAt(tab, i, e.next);
}
break;
}
pred = e;
if ((e = e.next) == null)
break;
}
}
//如果为树,很简单的实现
else if (f instanceof TreeBin) {
validated = true;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null &&
(p = r.findTreeNode(hash, key, null)) != null) {
V pv = p.val;
if (cv == null || cv == pv ||
(pv != null && cv.equals(pv))) {
oldVal = pv;
if (value != null)
p.val = value;
else if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
}
if (validated) {
if (oldVal != null) {
if (value == null)
addCount(-1L, -1);
return oldVal;
}
break;
}
}
}
return null;
}