Striped64阅读笔记
一、简介
Striped64是在java8中添加用来支持累加器的并发组件,它可以在并发环境下使用来做某种计数,Striped64的设计思路是在竞争激烈的时候尽量分散竞争,在实现上,Striped64维护了一个base Count和一个Cell数组,计数线程会首先试图更新base变量,如果成功则退出计数,否则会认为当前竞争是很激烈的,那么就会通过Cell数组来分散计数,Striped64根据线程来计算哈希,然后将不同的线程分散到不同的Cell数组的index上,然后这个线程的计数内容就会保存在该Cell的位置上面,基于这种设计,最后的总计数需要结合base以及散落在Cell数组中的计数内容。这种设计思路类似于java7的ConcurrentHashMap实现,也就是所谓的分段锁算法,ConcurrentHashMap会将记录根据key的hashCode来分散到不同的segment上,线程想要操作某个记录只需要锁住这个记录对应着的segment就可以了,而其他segment并不会被锁住,其他线程任然可以去操作其他的segment,这样就显著提高了并发度,虽然如此,java8中的ConcurrentHashMap实现已经抛弃了java7中分段锁的设计,而采用更为轻量级的CAS来协调并发,效率更佳。
二、继承关系图
继承了抽象Number类
三、存储结构
使用内部类Cell,内部的函数和属性的修饰的访问权限都是default,只能包内的对象访问
四、源码分析
内部类
//它是Striped64实现分散计数的最为基础的数据结构
@sun.misc.Contended static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
//调用unsafe.compareAndSwapLoing进行元素值替换
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}
// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long valueOffset;
static {
try {
//初始化Unsafe和valueOffset值
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Cell.class;
valueOffset = UNSAFE.objectFieldOffset
(ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
}
/*
value:cell的元素值
valueOffset:value在cell对象中的内存偏移量
UNSAFE:工具类
*/
属性
transient volatile Cell[] cells;//存储各阶段的值,
transient volatile long base;//最初无竞争时使用,也是一种特殊的段
transient volatile int cellsBusy;//标记当前是否有线程在创建cells、扩容cells、创建cell,通过cas更新该值,相当于一个锁
static final int NCPU = Runtime.getRuntime().availableProcessors(); //可用处理器数量,获取系统的可用进程来限制表的大小
//Unsafe mechanics unsafe和属性基础对象的偏移量
private static final sun.misc.Unsafe UNSAFE;
private static final long BASE;//base值在对象的内存偏移量
private static final long CELLSBUSY;//cellsBusy值在对象中的内存偏移量
private static final long PROBE;//threadLocalRandomProbe属性在Thread类中的内存偏移量
static {
try {
//初始化属性基础对象的内存偏移量
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> sk = Striped64.class;
BASE = UNSAFE.objectFieldOffset
(sk.getDeclaredField("base"));
CELLSBUSY = UNSAFE.objectFieldOffset
(sk.getDeclaredField("cellsBusy"));
Class<?> tk = Thread.class;
PROBE = UNSAFE.objectFieldOffset
(tk.getDeclaredField("threadLocalRandomProbe"));
} catch (Exception e) {
throw new Error(e);
}
}
构造
Striped64() {}
主要方法
-
longAccumulate
:针对long类型实现计数-
/** * Handles cases of updates involving initialization, resizing, * creating new Cells, and/or contention. See above for * explanation. This method suffers the usual non-modularity * problems of optimistic retry code, relying on rechecked sets of * reads. * * @param x the value * @param fn the update function, or null for add (this convention * avoids the need for an extra field or function in LongAdder). * @param wasUncontended false if CAS failed before call */ final void longAccumulate(long x, LongBinaryOperator fn, boolean wasUncontended) { int h; if ((h = getProbe()) == 0) {//代表probe没有初始化 ThreadLocalRandom.current(); // force initialization h = getProbe();//重新获取probe wasUncontended = true;//没有初始化,所以不存在锁竞争 } //是否发生碰撞,或者其他冲突 boolean collide = false; // True if last slot nonempty for (;;) { //as 获取ceels数组表 //a 获取当前线程cell,通过as[(n - 1) & h] //n 获取ceels数组大小 Cell[] as; Cell a; int n; long v; //一、判断cells是否为null,且长度是否大于0 if ((as = cells) != null && (n = as.length) > 0) { //1、如果取出的ceel == null 说明此锁未被占用 if ((a = as[(n - 1) & h]) == null) { //如果不在扩容和新建状态 if (cellsBusy == 0) { // Try to attach new Cell Cell r = new Cell(x); // Optimistically create //进行锁占用 if (cellsBusy == 0 && casCellsBusy()) { boolean created = false;//默认创建失败 try { // Recheck under lock Cell[] rs; int m, j; //再验证表是否为null和当前是否被占用 if ((rs = cells) != null && (m = rs.length) > 0 && rs[j = (m - 1) & h] == null) { //更新 rs[j] = r; created = true;//标识此次创建成功 } } finally { cellsBusy = 0;//释放当前锁 } if (created)//创建成功,退出 break; continue; //创建失败,不修改probe,重试 // Slot is now non-empty } } //标记当前没有出现碰撞 collide = false; } //2、当前线程不为null,且更新失败了 // 这里就是简单的自旋一次,然后下面修改probe然后重试 else if (!wasUncontended) // CAS already known to fail wasUncontended = true; // Continue after rehash //3、尝试cas更新一次,成功返回 else if (a.cas(v = a.value, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) break; //4、设置的cells数组的长度达到了cpu的核心数,或者cells扩容了 else if (n >= NCPU || cells != as) collide = false; // At max size or stale //5、 第 3 都更新失败,且第 4 也不成立,说明出现冲突了,就简单自选一次,重新开始 else if (!collide) collide = true; //6、明显出现冲突了,占有锁,并进行扩容 else if (cellsBusy == 0 && casCellsBusy()) { try { if (cells == as) { // Expand table unless stale Cell[] rs = new Cell[n << 1]; for (int i = 0; i < n; ++i) rs[i] = as[i]; cells = rs; } } finally { cellsBusy = 0;//解锁 } collide = false;//标记没有出现碰撞 continue; //扩容后,不修改probe,进行重试 // Retry with expanded table } //recall:更新失败,或达到CPU核心数,重新生成probe,并且重试 h = advanceProbe(h); } //二、就判断cellsbusy是否为0 (是否为无锁状态) // cells是否为null // 修改cellsbusy为1(尝试加锁,加锁成功true,否则false) else if (cellsBusy == 0 && cells == as && casCellsBusy()) { boolean init = false;//初始化默认不成功 try { // Initialize table if (cells == as) { Cell[] rs = new Cell[2];//新建一个大小为2的cell数组 rs[h & 1] = new Cell(x);//找到当前线程的hash在数组中对应的cell cells = rs;//把初始化的表赋值给cells属性 init = true;//初始化成功 } } finally { cellsBusy = 0;//初始化完成就修改cellsbusy为0(也是释放锁) } if (init)//初始化成功则终止cas break; } //三、如果与其他线程在初始化cells数组中,就尝试更新base // 如果更新成功则返回 else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) break; // Fall back on using base } }
-
-
doubleAccumulate
:针对double类型实现计数- 源码类似long
其他方法
-
getProbe
:获得Thread.currentThread()中的threadLocalRandomProbe值,用于重新随机获取probe值-
/** * Returns the probe value for the current thread. * Duplicated from ThreadLocalRandom because of packaging restrictions. */ static final int getProbe() { return UNSAFE.getInt(Thread.currentThread(), PROBE); }
-
-
advanceProbe
:修改thread中threadLocalRandomProbe的值为推进后的probe,并返回新的probe-
/** * Pseudo-randomly advances and records the given probe value for the * given thread. * Duplicated from ThreadLocalRandom because of packaging restrictions. */ static final int advanceProbe(int probe) { probe ^= probe << 13; // xorshift probe ^= probe >>> 17; probe ^= probe << 5; UNSAFE.putInt(Thread.currentThread(), PROBE, probe); return probe; }
-
-
casBase
:cas替换base属性-
//CASes the base field. final boolean casBase(long cmp, long val) { return UNSAFE.compareAndSwapLong(this, BASE, cmp, val); }
-
-
casCellsBusy
:如果cellsbusy为0则cas替换为1-
//CASes the cellsBusy field from 0 to 1 to acquire lock. final boolean casCellsBusy() { //如果cellsbusy属性是0,则修改为1,且返回true。否则返回false return UNSAFE.compareAndSwapInt(this, CELLSBUSY, 0, 1); }
-
补充
无