AtomicLong
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
}
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
do {
var6 = this.getLongVolatile(var1, var2);
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
}
//比较并替换
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
提高效率 使用 LongAdder
LongAdder 和 AtomicLong 比较
Demo1:
/**
* Atomic和LongAdder耗时测试
*/
public class AtomicLongAdderTest {
public static void main(String[] args) throws Exception{
testAtomicLongAdder(1, 10000000);
testAtomicLongAdder(10, 10000000);
testAtomicLongAdder(100, 10000000);
}
static void testAtomicLongAdder(int threadCount, int times) throws Exception{
System.out.println("threadCount: " + threadCount + ", times: " + times);
long start = System.currentTimeMillis();
testLongAdder(threadCount, times);
System.out.println("LongAdder 耗时:" + (System.currentTimeMillis() - start) + "ms");
System.out.println("threadCount: " + threadCount + ", times: " + times);
long atomicStart = System.currentTimeMillis();
testAtomicLong(threadCount, times);
System.out.println("AtomicLong 耗时:" + (System.currentTimeMillis() - atomicStart) + "ms");
System.out.println("----------------------------------------");
}
static void testAtomicLong(int threadCount, int times) throws Exception{
AtomicLong atomicLong = new AtomicLong();
List<Thread> list = new ArrayList();
for (int i = 0; i < threadCount; i++) {
list.add(new Thread(() -> {
for (int j = 0; j < times; j++) {
atomicLong.incrementAndGet();
}
}));
}
for (Thread thread : list) {
thread.start();
}
for (Thread thread : list) {
thread.join();
}
System.out.println("AtomicLong value is : " + atomicLong.get());
}
static void testLongAdder(int threadCount, int times) throws Exception{
LongAdder longAdder = new LongAdder();
List<Thread> list = new ArrayList();
for (int i = 0; i < threadCount; i++) {
list.add(new Thread(() -> {
for (int j = 0; j < times; j++) {
longAdder.increment();
}
}));
}
for (Thread thread : list) {
thread.start();
}
for (Thread thread : list) {
thread.join();
}
System.out.println("LongAdder value is : " + longAdder.longValue());
}
}
结果:
threadCount: 1, times: 10^7
LongAdder 耗时:107ms
AtomicLong 耗时:45ms
----------------------------------------
threadCount: 10, times: 10^7
LongAdder 耗时:58ms
AtomicLong 耗时:831ms
----------------------------------------
threadCount: 100, times: 10^7
LongAdder 耗时:443ms
AtomicLong 耗时:7959ms
----------------------------------------
当线程多了,LongAdder 的优势就体现出来了。
public class LongAdder extends Striped64 implements Serializable {
public void add(long x) {
//as 表示cells 引用
//b
//v 表示 期望值表示获取的base值
//m 表示cells 数组的长度
//a 表示当前线程命中的 cell单元格
Cell[] as; long b, v; int m; Cell a;
//条件一: true-> 表示cells已经初始化过了,当前线程应该将数据写入到对应的cell中
// false-> 表示cells 未初始化,当前所有线程应该将数据写入base中
//条件二:true-> 表示当前线程cas替换数据成功
// false-> 表示发生竞争了,可能需要重试 或者 扩容
//true 是指casBase(b = base, b + x),不是整体,是部分
//true时,取反是false
// 初始化过cells 或 发生竞争时需要重试或者扩容 才进入if
if ((as = cells) != null || !casBase(b = base, b + x)) {
//true 未竞争 false 发生竞争
boolean uncontended = true;
//条件一:true-> 说明 cells 未初始化,也就是多线程写base发生竞争
// false-> 说明 cells 已经初始化了,当前线程应该是 找自己的cell 写值
if (as == null || (m = as.length - 1) < 0 ||
//条件二:getProbe() 获取当前线程的hash值, m表示 cells长度-1 cells长度 一定是2的次方数 16-1=15=1111 二进制
// true-> 说明当前线程对应下标的cell 为空 ,需要创建longAccumulate 支持
// false-> 说明当前线程对应的cell 不为空,说明 下一步想要将x值,添加到cell中
(a = as[getProbe() & m]) == null ||
//条件三:整体而言,有取反,true-> uncontended为false cas操作失败,意味着当前线程对应的cell 有竞争
// false-> 表示cas成功
!(uncontended = a.cas(v = a.value, v + x)))
//哪些情况会调用
//true-> 说明 cells 未初始化,也就是多线程写base发生竞争
//true-> 说明当前线程对应下标的cell 为空 ,需要创建longAccumulate 支持
//true-> cas操作失败,意味着当前线程对应的cell 有竞争
longAccumulate(x, null, uncontended);
}
}
}
重点都在最后一个方法 longAccumulate
是Striped64 类的方法,这个方法是重点
最终方法解析 longAccumulate
abstract class Striped64 extends Number {
//1、true-> 说明 cells 未初始化,也就是多线程写base发生竞争
//2、true-> 说明当前线程对应下标的cell 为空 ,需要创建longAccumulate 支持
//3、true-> cas操作失败,意味着当前线程对应的cell 有竞争
//longAccumulate(x, null, uncontended);
//wasUncontended :只有cells 初始化之后,并且当前线程 竞争修改失败,才会是false
final void longAccumulate(long x, LongBinaryOperator fn,
boolean wasUncontended) {
// h 表示线程hash值
int h;
//条件成立:说明当前线程 还未分配线程
if ((h = getProbe()) == 0) {
//给当前线程分配hash值
ThreadLocalRandom.current(); // force initialization
//取出当前线程的hash值
h = getProbe();
// 因为默认情况下 , 肯定是写入到了 cells[0]位置,不把它当做一次真正的竞争
//因为没有hash值,所有才会在cells[0]竞争,所有表示一次真正意义上的竞争
wasUncontended = true;
}
//表示扩容意向 false 一定不会扩容,true 可能会扩容
boolean collide = false; // True if last slot nonempty
//自旋
for (;;) {
// as 表示cells引用
// a 表示当前线程命中的cell
// n 表示cells 数组长度
// v 表示 期望值
Cell[] as; Cell a; int n; long v;
// CASE1:表示cells已经初始化了,当前线程应该将数据写入到对应的cell中
if ((as = cells) != null && (n = as.length) > 0) {
//下面两种情况才会进入该if
//2、true-> 说明当前线程对应下标的cell 为空 ,需要创建longAccumulate 支持
//3、true-> cas操作失败,意味着当前线程对应的cell 有竞争
// CASE1.1: true->表示当前线程 对应的下标未知的cell 为null,需要创建new Cell
if ((a = as[(n - 1) & h]) == null) {
//true -> 表示当前 锁未被占用
if (cellsBusy == 0) { // Try to attach new Cell
//拿当前的x创建Cell
Cell r = new Cell(x); // Optimistically create
//条件一:true-> 表示当前锁未被占用 false -> 表示锁被占用
//条件二:true-> 表示当前线程获取锁成功, false-> 当前线程获取锁失败
if (cellsBusy == 0 && casCellsBusy()) {
boolean created = false;
try { // Recheck under lock
// rs 表示当前cells引用
// m 表示cells长度
// j 表示当前线程命中的下标
Cell[] rs; int m, j;
//条件一 条件二 恒成立
//rs[j = (m - 1) & h] == null 是为了防止其它线程初始化 该位置,然后当前线程再次初始化该位置
//在上面最近的if 时,其它线程可能抢走锁,让 rs[j]已经赋值,当该线程抢回锁之后可以防止重复赋值
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; // Slot is now non-empty
}
}
//扩容意向更改为false (a = as[(n - 1) & h]) == null
//因为当前cell为 null,cells够用,所以不需要扩容
collide = false;
}
//CASE1.2:
//只有当前线程的hash值不是0,并且和其它线程一起打在了同一个cell上时,wasUncontended才是false
else if (!wasUncontended) // CAS already known to fail
wasUncontended = true; // Continue after rehash
//CASE1.3:当前线程rehash 过 hash值,然后新命中的cell不为空
//true -> 写成功,退出循环
//false -> 表示rehash之后命中的cell 也有竞争,重试一次
else if (a.cas(v = a.value, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break;
//CASE1.4:
//条件一:n >= NCPU true-> 扩容意向为false,表示不扩容了 false-> 表示还可以扩容
//条件二:cells != as true -> 其它线程已经扩容过了,当前线程rehash
else if (n >= NCPU || cells != as)
//扩容意向:改为false,表示不扩容了
collide = false; // At max size or stale
//CASE 1.5:
//collide取反 设置扩容意向 为true,但不一定扩容
else if (!collide)
collide = true;
//CASE 1.6:真正的扩容
// 条件一:cellsBusy == 0 当前线程无锁状态,可以去竞争
// 条件二:casCellsBusy() 当前线程获取锁,true:可以做扩容逻辑,false:表示其它线程正在做扩容相关操作
else if (cellsBusy == 0 && casCellsBusy()) {
try {
//cells == as 当其它线程抢了线程,先扩容,当前线程抢回后,可以防止再次扩容
// 防止重复扩容
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; // Retry with expanded table
}
//重置hash值 rehash
h = advanceProbe(h);
}
// CASE2:前置条件cells 还未初始化 as 为 null
// 条件一:cellsBusy 表示未加锁
// 条件二:cells == as? 因为其他线程可能会在你给as赋值之后 修改了 cells
// 条件三:true 表示获取锁成功 会把cellsBusy = ,false表示其他线程正在持有锁
else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
//1、true-> 说明 cells 未初始化,也就是多线程写base发生竞争
boolean init = false;
try { // Initialize table
// cells == as 又要对比,防止再次初始化
// 防止其他线程已经初始化了,当前线程再次初始化 导致丢失数据
if (cells == as) {
Cell[] rs = new Cell[2];
rs[h & 1] = new Cell(x);
cells = rs;
init = true;
}
} finally {
cellsBusy = 0;
}
if (init)
break;
}
// CASE3:
// 1、当前cellBusy 加锁状态,表示其它线程正在初始化cells,所有当前线程将值累加到base
// 2、cells被其他线程初始化后,当前线程需要将数据累加到base
else if (casBase(v = base, ((fn == null) ? v + x :
fn.applyAsLong(v, x))))
break; // Fall back on using base
}
}
}