结构
JDK1.8 的实现摒弃了Segment
的概念,直接用Node数组 + 链表 + 红黑树的数据结构来实现,并发控制使用Synchronized
和CAS
来操作,
sizeCtl
用来控制table的初始化和扩容的操作,不同的值有不同的含义。
- table未初始化时:sizeCtl = 0或者 sizeCtl= capacity;
- table正在初始化:sizeCtl = -1;
- table初始化完成:sizeCtl = thresold;
- 当sizeCtl = − (1 + N),表明正在有N条线程正在进行resize操作;
put()方法
public V put(K key, V value) {
return putVal(key, value, false);
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
//hash操作
int hash = spread(key.hashCode());
int binCount = 0;
//bitCount表示i处的节点数量,用来判断是否转换成红黑树
for (Node<K,V>[] tab = table;;) {//CAS插入
Node<K,V> f; int n, i, fh;
//除非构造时指定初始化集合,否则默认构造不初始化table
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
//当前节点为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)
tab = helpTransfer(tab, f);
//当前节点正在扩容,让当前线程帮助扩容,扩容完指向新的table
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)
//onlyIfAbsent表示是新元素才加入,旧值不替换,默认为fase。
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;
}