[JDK源码]-J.U.C-ConcurrentHashMap
Posted L._l
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[JDK源码]-J.U.C-ConcurrentHashMap相关的知识,希望对你有一定的参考价值。
由于作者水平有限,如有什么错误点,多谢指出。
ConcurrentHashMap
public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
implements ConcurrentMap<K,V>, Serializable
//保存K-V 节点
static class Node<K,V> implements Map.Entry<K,V>
final int hash;
final K key;
volatile V val;
volatile Node<K,V> next;
//链表Node 转为 TreeNode 节点,继承了Node节点
static final class TreeNode<K,V> extends Node<K,V>
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev;
boolean red;
//链表Node转为红黑树,放在 数组位置上的头节点,本身不保存数据
static final class TreeBin<K,V> extends Node<K,V>
TreeNode<K,V> root;
volatile TreeNode<K,V> first;
volatile Thread waiter; //等待线程
volatile int lockState; //锁状态
static final int WRITER = 1; // 写锁
static final int WAITER = 2; // 等待写锁
static final int READER = 4; // 读锁
//保存数据的数组 2的倍数
transient volatile Node<K,V>[] table;
/*扩容时候生成的下一个数组 */
private transient volatile Node<K,V>[] nextTable;
/*计数器 */
private transient volatile long baseCount;
/*用于控制hash表的初始化和扩容,-1:hash初始化,-1+参与扩容操作的线程数:用于扩容操作,正数:hash表的容量 */
private transient volatile int sizeCtl;
/*在扩容时,多线程竞争转移 槽 区间时使用 */
private transient volatile int transferIndex;
/*在扩容时自旋锁使用 */
private transient volatile int cellsBusy;
/* CounterCell hash表,如果不为空,那么肯定是2的倍数 */
private transient volatile CounterCell[] counterCells;
// 通过迭代器遍历时候 的 几种视图
private transient KeySetView<K,V> keySet;
private transient ValuesView<K,V> values;
private transient EntrySetView<K,V> entrySet;
static final int MOVED = -1; // resize时 forwarding 节点 的hash值
static final int TREEBIN = -2; // 红黑树根节点的hash值
static final int RESERVED = -3; // 创建 reservationNode 节点的hash值
static final int HASH_BITS = 0x7fffffff; // 正常hash用的位数
static final class ReservationNode<K,V> extends Node<K,V>
ReservationNode()
super(RESERVED, null, null, null);
//创建初始容量的
public ConcurrentHashMap(int initialCapacity)
if (initialCapacity < 0)
throw new IllegalArgumentException();
//最大容量 MAXIMUM_CAPACITY = 1<<30
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
MAXIMUM_CAPACITY :
tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
//此时保存 hash表的最大容量
this.sizeCtl = cap;
//转移操作时,如果slot没有节点可以转移或已经转移成功,将在相应的slot放上这个类的对象
static final class ForwardingNode<K,V> extends Node<K,V>
final Node<K,V>[] nextTable;
ForwardingNode(Node<K,V>[] tab)
super(MOVED, null, null, null);
this.nextTable = tab;
//减少hash冲突,异或的结果继续和HASH_BITS 与运算
static final int spread(int h)
return (h ^ (h >>> 16)) & HASH_BITS;//0x7fffffff
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(); //不允许放入空值
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)//为空 初始化 hash表
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null)
//根据hash值找到应该保存的数组的位置,如果没放入,通过CAS,封装成 Node对象放入索引位 i的位置
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break;
//这里,f就是找到下标位i的node元素,这时根据MOVED标志看看是数组正在扩容还是数据迁移,如果迁移,那么调用helpTransfer方法帮助完成迁移
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else
V oldVal = null;
synchronized (f) //对f上锁
if (tabAt(tab, i) == f) //索引下标 i 处节点没有被更改
if (fh >= 0) //链表
binCount = 1;
for (Node<K,V> e = f;; ++binCount) //遍历链表
K ek;
//当前与 要放入的 hash 相同
if (e.hash == hash &&
((ek = e.key) == key || //比较 地址
//比较 equals
(ek != null && key.equals(ek))))
oldVal = e.val; //保存找到的旧值
if (!onlyIfAbsent)//如果没有设置 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)
//红黑树,在 slot中放置了一个 TreeBin对象 ,然后才是根节点 所以直接设置2
Node<K,V> p;
binCount = 2;
//插入红黑树,如果返回值不为空,说明红黑树包含了K,根据onlyIfAbsent 来决定是否覆盖原来的值
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null)
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
if (binCount != 0)
//节点超过 TREEIFY_THRESHOLD = 8
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);//转换为红黑树
if (oldVal != null)//如果发生了冲突,替换原来的值,则返回
return oldVal;
break;
addCount(1L, binCount);//增加一个节点计数,看看是否需要扩容
return null;
initTable初始化过程
private final Node<K,V>[] initTable()
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) //循环直到成功
if ((sc = sizeCtl) < 0)
Thread.yield();
else if (U.compareAndSwapInt(this, SIZECTL, sc, -1))
try //双重判断,避免CAS成功,但后面释放了sc后 又有线程进入
if ((tab = table) == null || tab.length == 0)
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;//默认16
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];//创建数组
table = tab = nt;
//sc保存最大的数量,这个n是 0.75
sc = n - (n >>> 2);
finally
sizeCtl = sc;
break;
return tab;
addCount
private final void addCount(long x, int check)
CounterCell[] as; long b, s;
if ((as = counterCells) != null ||//已经创建
//直接在baseCount 变量上计数。如果失败进入初始化 as,
!U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x))
CounterCell a; long v; int m;
boolean uncontended = true;
//初始化as
if (as == null || (m = as.length - 1) < 0 ||
(a = as[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x)))
fullAddCount(x, uncontended);//创建as或者添加计数器
return;
if (check <= 1)//如果check<= 1 直接退出
return;
s = sumCount();//获取当前hash表的数据量
if (check >= 0) //检查表的大小,要不要扩容
Node<K,V>[] tab, nt; int n, sc;
//s是当前数据量,大于等于 当前容量 && 数组不为空 && 数组小于最大容量
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY)
int rs = resizeStamp(n); //计算扩容时 使用的stamp
if (sc < 0) //如果sc小于0,表明已经开始扩容
//stamp 已经改变 || 达到最大帮助resize的线程数 MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
//nextTable 为空,扩容完毕
if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
transferIndex <= 0)
break;
//增加帮助 resize 的线程数
if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt); //如果成功 开始扩容
//将 sc设置为 (rs << RESIZE_STAMP_SHIFT) + 2)
else if (U.compareAndSwapInt(this, SIZECTL, sc,
(rs << RESIZE_STAMP_SHIFT) + 2))
transfer(tab, null);//开始扩容
s = sumCount(); //重新计算 数据量
transfer扩容
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab)
int n = tab.length, stride;
//计算每个线程负责的区间,单核就单线程
//否则(n >>> 3) / NCPU。 最小值MIN_TRANSFER_STRIDE = 16
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE;
if (nextTab == null) // 初始化目标数组
try
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<以上是关于[JDK源码]-J.U.C-ConcurrentHashMap的主要内容,如果未能解决你的问题,请参考以下文章