Unsafe
ConcurrentHashMap使用Synchronized和CAS锁对线程进行并发控制。CAS锁,也就是自旋锁,即比较并交换。ConcurrentHashMap的CAS锁是使用jdk.internal.misc.Unsafe类实现的,而jdk.internal.misc.Unsafe实际是调用sun.misc.Unsafe。
sun.misc.Unsafe的无参构造函数是私有private的,所以不能直接通过new实例化的方式获得一个对象,只能通过反射的方式获取。sun.misc.Unsafe类提供了三个CAS方法:compareAndSwapInt、compareAndSwapLong和compareAndSwapObject,分别是对int、long和object类型的变量做比较交换。这三个方法都有4个入参,第一个参数表示要作用的对象,第二个参数表示要作用的变量,第三个参数表示期望值,第四个参数是修改变成的值,当变量的值与第三个参数相同时,才会将变量的值修改为第四个参数,如果修改成功会返回true,修改失败返回false。示例如下:
import sun.misc.Unsafe;
import java.lang.reflect.Field;
class Person {
public int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
@Test
public void testUnsafe() {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Person person = new Person();
try {
Unsafe unsafe = (Unsafe) unsafeField.get(null);
//定义要作用在哪个变量上
final long COUNT_OFFSET = unsafe.objectFieldOffset(person.getClass().getDeclaredField("age"));
boolean b = unsafe.compareAndSwapInt(person, COUNT_OFFSET, 0, 1);
System.out.println(b);
System.out.println(person.getAge());
} catch (Exception e) {
e
.printStackTrace();
}
}
comparableClassFor(Object x)
假设对象x的Class类型为C,如果Class C实现了Comparable接口,Comparable有且仅有一个泛型参数,是这个类本身C,也就是:
class C implements Comparable<C>
调用ConcurrentHashMap的comparableClassFor()方法会返回C.class。
static Class<?> comparableClassFor(Object x) {
//判断是否是Comparable的子类,如果不是直接返回null
if (x instanceof Comparable) {
Class<?> c; Type[] ts, as; ParameterizedType p;
//如果参数x的Class类型是String,直接返回class java.lang.String
if ((c = x.getClass()) == String.class) // bypass checks
return c;
//获取实现的父类接口类型
if ((ts = c.getGenericInterfaces()) != null) {
//遍历接口类型
for (Type t : ts) {
//判断该接口是否实现了泛型 && 接口的类型对象是否是Comparable
// 泛型参数只有一个 && 泛型参数类型就是入参的Class类型
if ((t instanceof ParameterizedType) &&
((p = (ParameterizedType)t).getRawType() == Comparable.class) && (as = p.getActualTypeArguments()) != null &&
as.length == 1 && as[0] == c)
return c;
}
}
}
return null;
}
Class的getGenericInterfaces()方法返回的是实现的接口的类型数组。
public Type[] getGenericInterfaces() {
ClassRepository info = getGenericInfo();
return (info == null) ? getInterfaces() : info.getSuperInterfaces();
}
接口ParameterizedType继承了接口Type。ParameterizedType是带有类型参数的类型,即常说的泛型,如Collection。
Type[] getActualTypeArguments(); //返回一个Type数组,数组里是参数化类型的参数
Type getRawType();//返回声明此类型的类或接口
示例:
List<String> list = new ArrayList<String>();
Type[] genericInterfaces = list.getClass().getGenericInterfaces();
for(Type type:genericInterfaces) {
if(type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
System.out.println(Arrays.asList(parameterizedType.getActualTypeArguments()));//[E]
System.out.println(parameterizedType.getRawType());//interface java.util.List
}
}
compareComparables(Class<?> kc, Object k, Object x)
compareComparables是用来比较两个对象的大小。如果x为空,或者类型不是kc,返回0。如果x不为空并且x的类型是kc,返回k.compareTo(x)的比较结果。
static int compareComparables(Class<?> kc, Object k, Object x) {
return (x == null || x.getClass() != kc ? 0 :
((Comparable)k).compareTo(x));
}
ConcurrentHashMap插入元素
ConcurrentHashMap.put()方法会调用putVal()方法插入元素。putVal()方法的第三个布尔变量onlyIfAbsent如果为true,表示只有在不存在相同的元素(hashcode和key值相等)才允许插入。如果存在相同的元素,putVal()方法会返回旧值,否则,返回null。与HashMap不同的是,ConcurrentHashMap不支持null键和null值。
(1)如果数组为null或者长度为0,初始化数组。
(2)根据hash算出待插入元素存放在数组的下标,判断该下标是否存在元素,如果不存在,使用CAS锁直接插入元素。
(3)判断数组是否正在扩容,如果是,当前线程加入扩容。
(4)判断该下标位置上的元素与待插入元素的hash和key是否相等,如果相等,表示是同一个元素,直接返回。
(5)对该下标位置的元素进行Synchronized同步,遍历链表/红黑树,插入元素。链表使用尾插法。
(6)计数。
final V putVal(K key, V value, boolean onlyIfAbsent) {
//如果key或者value为null,抛出空指针异常。
if (key == null || value == null) throw new NullPointerException();
//将高16位和低16位进行异或运算,目标是得到尽可能不同的值。
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
//i表示待插入元素放在数组位置的下标,f表示tab[i]的元素,n表示tab的长度
//fh表示tab[i]元素的hash值,fk表示tab[i]元素的key,fv表示tab[i]元素的value值
Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
//当数组为空或者数组的长度为0,对数组进行初始化
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
//如果f为空,表示tab[i]没有存放元素,使用cas锁,期望值是null,最后替换为待插入节点(hash, key, value)
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break; //跳出循环,向空槽中添加元素不会使用锁
}
else if ((fh = f.hash) == MOVED)
//如果tab[i]的hash值是MOVED(-1),表示正在进行扩容,当前线程也会加入扩容
tab = helpTransfer(tab, f);
else if (onlyIfAbsent
&& fh == hash
&& ((fk = f.key) == key || (fk != null && key.equals(fk)))
&& (fv = f.val) != null)
//检查第一个节点不需要占用锁,onlyIfAbsent表示不存在才能添加
//如果onlyIfAbsent为true,并且tab[i]的hash值等于待插入节点的hash值,并且tab[i]的key的地址或内容等于待插入节点的hash值,
//并且tab[i]的value不为空,表示数组已经存在这个元素,直接返回。
return fv;
else {
V oldVal = null;
//对tab[i]的第一个节点做同步,相同数组不同的下标不会有影响。
synchronized (f) {
//判断tab[i]的元素是否是f,防止有其他线程做了修改
if (tabAt(tab, i) == f) {
if (fh >= 0) {
//用于计算tab[i]存放元素的数量
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记录的是旧值。
oldVal = e.val;
if (!onlyIfAbsent)
//如果允许替换旧值,直接替换旧值。
e.val = value;
break;
}
Node<K,V> pred = e;
//e执行自己的下一个节点,如果为空,表示到达了链表的尾部,直接插入新的元素。
if ((e = e.next) == null) {
//用的是尾插法,将待插入元素添加到链表尾部。
pred.next = new Node<K,V>(hash, key, value);
break;
}
}
}
else if (f instanceof TreeBin) {
//如果tab[i]节点是树节点。
Node<K,V> p;
binCount = 2;
//如果红黑树中已经存在该节点,putTreeVal返回该节点,否则返回null。
if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
value)) != null) {
//新值替换旧值
oldVal = p.val;
if (!onlyIfAbsent)
p.val = value;
}
}
else if (f instanceof ReservationNode)
throw new IllegalStateException("Recursive update");
}
}
if (binCount != 0) {
//如果tab[i]链表的长度大于TREEIFY_THRESHOLD,进行处理(扩容或者转成红黑树)
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
if (oldVal != null)
return oldVal;
break;
}
}
}
//数量加1之后判断是否需要扩容
addCount(1L, binCount);
return null;
}
table数组初始化
table是存储插入元素的数组,会延迟初始化直到第一次插入。大小总是2的幂,允许迭代器直接访问。
transient volatile Node<K,V>[] table;
HashMap有一个加载因子属性,默认是0.75f,HashMap扩容的阈值是等于数组长度乘以加载因子,当数组存储的元素的数量大于阈值就会进行扩容。而ConcurrentHashMap有一个属性sizeCtl,用于ConcurrentHashMap数组初始化和扩容控制。如果为负,表示正在进行初始化或者扩容;-1表示初始化,否则-(1+扩容线程数)。创建时,可以将sizeCtl设置为要使用的初始数组大小,默认为0。初始化后,sizeCtl为下一次数组调整的阈值。
未初始化:sizeCtl=0:表示没有指定初始容量。sizeCtl>0:表示初始容量。
初始化中:sizeCtl=-1,标记作用,告知其他线程,正在初始化。
正常状态:sizeCtl=0.75n ,扩容阈值。
扩容中: sizeCtl < 0 : 表示有其他线程正在执行扩容。
sizeCtl = (resizeStamp(n) << RESIZE_STAMP_SHIFT) + 2 :表示此时只有一个线程在执行扩容
private transient volatile int sizeCtl;
Thread.yield()方法的作用是让出当前线程的CPU时间片,使正在运行中的线程变成就绪状态,并重新竞争CPU的调度权,它可能会获取到,也有可能被其他线程获取到。
private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
//如果sizeCtl小于0,表示当前有线程正在进行初始化
if ((sc = sizeCtl) < 0)
Thread.yield(); // 当前线程释放cpu
//使用CAS锁,将全局变量sizeCtl的值修改为-1
else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
//如果sc的值是0,设置为16
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
//创建新数组,并赋值
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n];
table = tab = nt;
//n >>> 2表示1/4的n,n - (n/4) = 0.75n
sc = n - (n >>> 2);
}
} finally {
//sizeCtl表示阈值
sizeCtl = sc;
}
break;
}
}
return tab;
}
数组扩容
transferIndex是扩容索引,表示已经分配给扩容线程的table数组索引位置。transferIndex主要用来协调多个线程,并发安全地获取迁移任务(hash桶)。
private transient volatile int transferIndex;
final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) {
Node<K,V>[] nextTab; int sc;
//如果f节点属于ForwardingNode类型,表示对应的桶已经迁移完毕
if (tab != null && (f instanceof ForwardingNode) && (nextTab = ((ForwardingNode<K,V>)f).nextTable) != null) {
int rs = resizeStamp(tab.length) << RESIZE_STAMP_SHIFT;
//sizeCtl的值小于0,表示正在进行扩容
while (nextTab == nextTable && table == tab && (sc = sizeCtl) < 0) {
//
if (sc == rs + MAX_RESIZERS || sc == rs + 1 || transferIndex <= 0)
break;
//CAS锁修改sizeCtl的值
if (U.compareAndSetInt(this, SIZECTL, sc, sc + 1)) {
//迁移
transfer(tab, nextTab);
break;
}
}
return nextTab;
}
return table;
}
获取可用的线程数。
static final int NCPU = Runtime.getRuntime().availableProcessors();
//将数据从旧数组迁移到新数组
private final void transfer(Node<K,V>[] tab, Node<K,V>[] nextTab) {
//n表示旧数组的长度,stride表示每次要迁移的hash桶的个数
int n = tab.length, stride;
//如果可用的线程数大于1,stride等于(n/8)除以NCPU的值;否则stride等于n
//如果计算出来的stride小于MIN_TRANSFER_STRIDE(16),将stride设置为16,所以每次要迁移的hash桶的个数至少为16
if ((stride = (NCPU > 1) ? (n >>> 3) / NCPU : n) < MIN_TRANSFER_STRIDE)
stride = MIN_TRANSFER_STRIDE;
//如果新数组为null,要进行初始化
if (nextTab == null) {
try {
//创建新数组,长度扩大一倍
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n << 1];
//赋值给newTab
nextTab = nt;
} catch (Throwable ex) {
sizeCtl = Integer.MAX_VALUE;
return;
}
//将ConcurrentHashMap的变量属性nextTable的值设置为nextTab
nextTable = nextTab;
//迁移数据从旧数组的最右边开始
transferIndex = n;
}
//nextn表示新数组的长度
int nextn = nextTab.length;
//ForwardingNode继承了Node类,ForwardingNode的构造方法将hash值设置为MOVED=1,同时将入参赋值给ConcurrentHashMap属性nextTable
ForwardingNode<K,V> fwd = new ForwardingNode<K,V>(nextTab);
//advance表示当前线程是否需要继续转移数据
boolean advance = true;
//finishing表示当前线程的转移工作是否完成
boolean finishing = false;
//假设transferIndex = 32
//线程A:nextIndex = 32, nextBound = 16,transferIndex = 16, bound = 16, i = 31, advance = false
//线程B:nextIndex = 16, nextBound = 0,transferIndex = 0,bound = 0, i = 15,advance = false
//线程C:--i = 14 > bound, advance = false
for (int i = 0, bound = 0;;) {
Node<K,V> f; int fh;
while (advance) {
int nextIndex, nextBound;
if (--i >= bound || finishing)
advance = false;
else if ((nextIndex = transferIndex) <= 0) {
i = -1;
advance = false;
}
else if (U.compareAndSetInt
(this, TRANSFERINDEX, nextIndex,
nextBound = (nextIndex > stride ?
nextIndex - stride : 0))) {
bound = nextBound;
i = nextIndex - 1;
advance = false;
}
}
if (i < 0 || i >= n || i + n >= nextn) {
int sc;
if (finishing) {
nextTable = null;
//把扩容迁移数据后的数组赋值给table。
table = nextTab;
//(n << 1) = 2n, (n >>> 1) = 0.5n,sizeCtl = 1.5n,扩容后的数组长度是2n,也就是2n * 0.75 = 1.5n
sizeCtl = (n << 1) - (n >>> 1);
return;
}
if (U.compareAndSetInt(this, SIZECTL, sc = sizeCtl, sc - 1)) {
//当前不是只有一个线程在扩容
if ((sc - 2) != resizeStamp(n) << RESIZE_STAMP_SHIFT)
return;
//只有一个线程时,表示完成。
finishing = advance = true;
i = n; // recheck before commit
}
}
else if ((f = tabAt(tab, i)) == null)
//如果tab[i]为null,将tab[i]设置为forwardingNode,表示迁移数据完成
advance = casTabAt(tab, i, null, fwd);
else if ((fh = f.hash) == MOVED)
//正在迁移数据
advance = true; // already processed
else {
//对tab[i]桶同步
synchronized (f) {
//再次验证tab[i]是否等于f
if (tabAt(tab, i) == f) {
//迁移到新数据组后,元素要么继续放在低位i,要么放在(i+n)位置上
Node<K,V> ln, hn;
//hash大于0表示是链表
if (fh >= 0) {
//fh & n 得到的结果是0或者1
int runBit = fh & n;
Node<K,V> lastRun = f;
//这个for循环是为了找出最后一段hash & n 不变的链表,这样最后一段链表就不需要重新创建结点了。
for (Node<K,V> p = f.next; p != null; p = p.next) {
int b = p.hash & n;
if (b != runBit) {
runBit = b;
lastRun = p;
}
}
if (runBit == 0) {
//rubBit等于0表示元素放在nextTab[i]上
ln = lastRun;
hn = null;
}
else {
//runBit等于1表示元素放在nextTab[i+n]上
hn = lastRun;
ln = null;
}
//头插法,lastRun之前的节点hash & n不确定,所以需要全部迁移
for (Node<K,V> p = f; p != lastRun; p = p.next) {
int ph = p.hash; K pk = p.key; V pv = p.val;
if ((ph & n) == 0)
ln = new Node<K,V>(ph, pk, pv, ln);
else
hn = new Node<K,V>(ph, pk, pv, hn);
}
//低位链表放在i处
setTabAt(nextTab, i, ln);
//高位链表放在i+n处
setTabAt(nextTab, i + n, hn);
//在原table中设置ForwardingNode节点以提示该桶扩容完成。
setTabAt(tab, i, fwd);
advance = true;
}
else if (f instanceof TreeBin) {
//如果是红黑树
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> lo = null, loTail = null;
TreeNode<K,V> hi = null, hiTail = null;
int lc = 0, hc = 0;
for (Node<K,V> e = t.first; e != null; e = e.next) {
int h = e.hash;
TreeNode<K,V> p = new TreeNode<K,V>
(h, e.key, e.val, null, null);
//尾插法 放在nextTab[i] 双向链表
if ((h & n) == 0) {
if ((p.prev = loTail) == null)
lo = p;
else
loTail.next = p;
loTail = p;
//lc表示存放在nextTab[i]上元素的数量
++lc;
}
else {
//放在nextTab[i+n]
if ((p.prev = hiTail) == null)
hi = p;
else
hiTail.next = p;
hiTail = p;
//hc表示存在在nextTab[i+n]上元素的数量
++hc;
}
}
//如果拆分后的树的节点数量已经少于6个就需要重新转化为链表
//如果hc等于0,表示tab[i]上全部数据都放在nextTab[i]上
ln = (lc <= UNTREEIFY_THRESHOLD) ? untreeify(lo) :
(hc != 0) ? new TreeBin<K,V>(lo) : t;
//如果lc等于0,表示tab[i]上全部数据都放在nextTab[i+n]上
hn = (hc <= UNTREEIFY_THRESHOLD) ? untreeify(hi) :
(lc != 0) ? new TreeBin<K,V>(hi) : t;
setTabAt(nextTab, i, ln);
setTabAt(nextTab, i + n, hn);
setTabAt(tab, i, fwd);
advance = true;
}
else if (f instanceof ReservationNode)
throw new IllegalStateException("Recursive update");
}
}
}
}
}
链表树化
当数组的长度大于64,且某个桶的链表的长度大于等于8,会将这个桶的链表转成红黑树,目的是提高元素的查找速度。红黑树的查找时间是logN,而链表的查找时间是N/2。当N等于8时,logN < N/2。
static final int MIN_TREEIFY_CAPACITY = 64;
/**
* 如果数组的长度小于64,进行扩容;
* 否则,将tab[index]的链表转成红黑树
*/
private final void treeifyBin(Node<K,V>[] tab, int index) {
//b保存的是tab[index],n是数组的长度
Node<K,V> b; int n;
//判断数组是否为空
if (tab != null) {
//判断数组的长度是否小于MIN_TREEIFY_CAPACITY(64)
if ((n = tab.length) < MIN_TREEIFY_CAPACITY)
//扩容一倍
tryPresize(n << 1);
//如果链表已经转化成红黑树,节点的hash值会变成-2。
//此时判断hash是否大于等于0,大于等于0表示还是链表
else if ((b = tabAt(tab, index)) != null && b.hash >= 0) {
//对b进行同步
synchronized (b) {
//再次判断tab[index]是否还是等于b
if (tabAt(tab, index) == b) {
//建立一个双向链表
//hd表示头节点,tl表示前驱节点
TreeNode<K,V> hd = null, tl = null;
for (Node<K,V> e = b; e != null; e = e.next) {
TreeNode<K,V> p =
new TreeNode<K,V>(e.hash, e.key, e.val,
null, null);
if ((p.prev = tl) == null)
//没有前驱节点的时候,将头节点设置为当前节点
hd = p;
else
//否则将前驱节点的后继节点设置为当前节点
tl.next = p;
//最后将当前节点设置为前驱节点,继续下一层循环
tl = p;
}
//new TreeBin(hd)是生成一棵红黑树,再将这颗树放在tab[index]位置上。
setTabAt(tab, index, new TreeBin<K,V>(hd));
}
}
}
}
}
ConcurrentHashMap计数加1
先来看下baseCount和CounterCell的定义:
baseCount记录的是整个哈希表中存储的结点的个数总和。ConcurrentHashMap对这个变量的操作是基于CAS的,在高并发的情况下,可能会更新失败,操作失败之后就会使用CounterCell数组进行计数,数组内的每个ConuterCell都是一个独立的计数单元。
注解@sun.misc.Contended用于解决伪共享问题。所谓伪共享,即是在同一缓存行(CPU缓存的基本单位)中存储了多个变量,当其中一个变量被修改时,就会影响到同一缓存行内的其他变量,导致它们也要跟着被标记为失效,其他变量的缓存命中率将会受到影响。解决伪共享问题的方法一般是对该变量填充一些无意义的占位数据,从而使它独享一个缓存行。
private transient volatile long baseCount;
private transient volatile CounterCell[] counterCells;
@jdk.internal.vm.annotation.Contended static final class CounterCell {
volatile long value;
CounterCell(long x) {
value = x; }
}
sumCount()方法是用来统计哈希表的数量,ConcurrentHashMap的size()方法也是直接调用了sumCount()方法。
//统计baseCount和CounterCell每一个cell的value值的总和
final long sumCount() {
//获取CounterCell数组
CounterCell[] cs = counterCells;
//获取baseCount变量的值
long sum = baseCount;
if (cs != null) {
//如果CounterCell完成初始化不为空,就遍历统计每一个cell的value值。
for (CounterCell c : cs)
if (c != null)
sum += c.value;
}
return sum;
}
private final void addCount(long x, int check) {
//cs记录变量counterCells的值,b记录变量baseCount的值,s是CAS修改baseCount后的期望值
CounterCell[] cs; long b, s;
//如果counterCells为空或者CAS修改baseCount值失败
if ((cs = counterCells) != null ||
!U.compareAndSetLong(this, BASECOUNT, b = baseCount, s = b + x)) {
//c是某个CounterCell,m是cs的最大下标索引,v是计算出的CounterCell的value值。
CounterCell c; long v; int m;
//uncontended表示修改cell的值是成功还是失败
boolean uncontended = true;
//如果cs为null,或者cs的长度等于0,或者CounterCell为null,或者CAS修改cell的值失败
//ThreadLocalRandom.getProbe() 得到线程的探针哈希值,与m进行与运算获得在CounterCell数组的下标
if (cs == null || (m = cs.length - 1) < 0 ||
(c = cs[ThreadLocalRandom.getProbe() & m]) == null ||
!(uncontended =
U.compareAndSetLong(c, CELLVALUE, v = c.value, v + x))) {
fullAddCount(x, uncontended);
return;
}
//当check的值小于等于1,只需要判断CAS修改cell的value值是否成功
if (check <= 1)
return;
s = sumCount();
}
//当check的值小于0时,不需要检查是否需要扩容
if (check >= 0) {
//tab记录哈希数组,n是哈希数组的长度,sc是扩容阈值,nt表示扩容后要迁移到的新数组
Node<K,V>[] tab, nt; int n, sc;
//如果哈希数组的数量大于等于扩容阈值,并且哈希数组不为空,并且哈希数组的长度小于最大容量上限
while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
(n = tab.length) < MAXIMUM_CAPACITY) {
int rs = resizeStamp(n) << RESIZE_STAMP_SHIFT;
if (sc < 0) {
if (sc == rs + MAX_RESIZERS || sc == rs + 1 ||
(nt = nextTable) == null || transferIndex <= 0)
break;
if (U.compareAndSetInt(this, SIZECTL, sc, sc + 1))
transfer(tab, nt);
}
else if (U.compareAndSetInt(this, SIZECTL, sc, rs + 2))
//扩容
transfer(tab, null);
s = sumCount();
}
}
}
//获取CPU核心线程数
static final int NCPU = Runtime.getRuntime().availableProcessors();
private final void fullAddCount(long x, boolean wasUncontended) {
int h;
//如果调用ThreadLocalRandom.getProbe() 得到的线程的探针哈希值是0
if ((h = ThreadLocalRandom.getProbe()) == 0) {
//调用ThreadLocalRandom.localInit()方法初始化当前线程的线程字段,指示生成线程本地seed值。
ThreadLocalRandom.localInit();
//获取线程探针哈希值
h = ThreadLocalRandom.getProbe();
//
wasUncontended = true;
}
boolean collide = false;
for (;;) {
CounterCell[] cs; CounterCell c; int n; long v;
if ((cs = counterCells) != null && (n = cs.length) > 0) {
if ((c = cs[(n - 1) & h]) == null) {
if (cellsBusy == 0) {
//创建一个CounterCell
CounterCell r = new CounterCell(x);
//如果cellsBusy的值是0,并且CAS锁修改cellsBusy的值成功(期望值是0,最后修改为1)
if (cellsBusy == 0 &&
U.compareAndSetInt(this, CELLSBUSY, 0, 1)) {
//created表示是否已经创建
boolean created = false;
try {
CounterCell[] rs; int m, j;
//再次校验,将新创建的CounterCell赋值到rs[j]位置上
if ((rs = counterCells) != null &&
(m = rs.length) > 0 &&
rs[j = (m - 1) & h] == null) {
rs[j] = r;
//创建成功
created = true;
}
} finally {
//把cellBusy的值修改为0
cellsBusy = 0;
}
//创建成功跳出循环
if (created)
break;
//如果再次校验的时候,rs[j]位置不等于null,进入下一层循环
continue;
}
}
collide = false;
}
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
else if (U.compareAndSetLong(c, CELLVALUE, v = c.value, v + x))
break;
else if (counterCells != cs || n >= NCPU)
collide = false; // At max size or stale
else if (!collide)
collide = true;
else if (cellsBusy == 0 &&
U.compareAndSetInt(this, CELLSBUSY, 0, 1)) {
try {
if (counterCells == cs)
//扩容数组一倍,并复制数组
counterCells = Arrays.copyOf(cs, n << 1);
} finally {
cellsBusy = 0;
}
collide = false;
continue; // Retry with expanded table
}
//重新hash
h = ThreadLocalRandom.advanceProbe(h);
}
//如果cellBusy的值是0,并且counterCells为null,并且CAS修改cellBusy的值为1成功
else if (cellsBusy == 0 && counterCells == cs &&
U.compareAndSetInt(this, CELLSBUSY, 0, 1)) {
//设置init值为false
boolean init = false;
try {
//counterCells为null
if (counterCells == cs) {
//创建数量为2的CounterCell数组
CounterCell[] rs = new CounterCell[2];
//h & 1的结果为0或者1
rs[h & 1] = new CounterCell(x);
//将rs赋值给counterCells
counterCells = rs;
//设置init值为true
init = true;
}
} finally {
//修改cellsBusy为0
cellsBusy = 0;
}
//如果初始化成功,跳出循环
if (init)
break;
}
//CAS修改baseCount的值成功,跳出循环
else if (U.compareAndSetLong(this, BASECOUNT, v = baseCount, v + x))
break;
}
}
参考
HashMap.comparableClassFor(Object x)方法解读
jdk1.8 hashmap.compareComparables方法解析
JDK8:HashMap源码解析:TreeNode类的find方法
jdk1.8 hashmap.tieBreakOrder方法解析
JAVA语言之ConcurrentHashMap1.8源码分析
ConcurrentHashMap源码分析(JDK8) 扩容实现机制
Map 大家族的那点事儿 ( 7 ) :ConcurrentHashMap ( 下 )
为并发而生的 ConcurrentHashMap(Java 8)
【Type】类型 ParameterizedType
ConcurrentHashMap分析(二)数据扩容