专栏系列文章地址:https://blog.csdn.net/qq_26437925/article/details/145290162
本文目标:
- 理解分段锁思想,了解LongAdder的原理
目录
- LongAdder
- 基本原理
- 源码分析
- unsafe int 操作的一些方法
- Cell 对象(`Striped64`类下的静态内部类)
- add(long x)
- longAccumulate(x, null, uncontended)
- sum()
- `LongAdder`和`AtomicLong`
LongAdder
基本原理
LongAdder内部使用了分段锁和Cell数组的机制来实现高效的并发累加。它将对一个变量的累加操作分散到多个Cell中,每个Cell维护一个独立的long值。在进行累加操作时,会根据一定的哈希算法将线程分配到不同的Cell上进行操作,这样可以减少多个线程同时访问同一个变量时的竞争,从而提高并发性能。
当多个线程并发地对LongAdder进行累加操作时,不同的线程可能会被分配到不同的Cell上进行操作,只有当多个线程同时访问同一个Cell时才需要进行同步操作。这种设计大大降低了锁的竞争程度,提高了并发性能。
java">/*** One or more variables that together maintain an initially zero* {@code long} sum. When updates (method {@link #add}) are contended* across threads, the set of variables may grow dynamically to reduce* contention. Method {@link #sum} (or, equivalently, {@link* #longValue}) returns the current total combined across the* variables maintaining the sum.*
- 只能做累加,或者自增自减操作,不能做其它操作
- 用一个
Cell
数组来存放分段数据值大小,Cell数组元素只有一个volatile long value
表示存放的值 sum方法
用于返回当前计数值,返回所有Cell中value的和- 多个线程会进行hash,对不同的Cell元素进行操作
- 内部有扩容方法,增加更多的Cell元素
英语单词:
- probe 美[proʊb]: v. 盘问; 追问; 探究; (用细长工具) 探查,查看;n.探究; 详尽调查; (不载人) 航天探测器,宇宙探测航天器; (医生用的) 探针;
- contention 美[kənˈtenʃn]: n. 争吵; 争执; 争论; (尤指争论时的) 看法,观点;
源码分析
unsafe int 操作的一些方法
- public native int getInt(Object o, long offset);//获得给定对象偏移量上的int值
- public native void putInt(Object o, long offset, int x);//设置给定对象偏移量上的int值
- public native long objectFieldOffset(Field f);//获得字段在对象中的偏移量
- public native void putIntVolatile(Object o, long offset, int x);//设置给定对象的int值,使用volatile语义
- public native int getIntVolatile(Object o, long offset);//获得给定对象对象的int值,使用volatile语义
- public native void putOrderedInt(Object o, long offset, int x);//和putIntVolatile()一样,但是它要求被操作字段就是volatile类型的
Cell 对象(Striped64
类下的静态内部类)
- 使用了
@sun.misc.Contended
注解(缓存行使用,解决伪共享问题)
java">/*** Padded variant of AtomicLong supporting only raw accesses plus CAS.** JVM intrinsics note: It would be possible to use a release-only* form of CAS here, if it were provided.*/
@sun.misc.Contended static final class Cell {volatile long value;Cell(long x) { value = x; }final boolean cas(long cmp, long val) {return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);}// Unsafe mechanicsprivate static final sun.misc.Unsafe UNSAFE;private static final long valueOffset;static {try {UNSAFE = sun.misc.Unsafe.getUnsafe();Class<?> ak = Cell.class;valueOffset = UNSAFE.objectFieldOffset(ak.getDeclaredField("value"));} catch (Exception e) {throw new Error(e);}}
}
Striped64
静态初始化
java">// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long BASE;
private static final long CELLSBUSY;
private static final long PROBE;
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);}
}
- base:为非竞争状态下的一个值
- PROBE:一个随机的hashCode一样,每个线程有一个自己的probe,可以标识cells数组下标
add(long x)
java">/*** Adds the given value.** @param x the value to add*/
public void add(long x) {Cell[] as; long b, v; int m; Cell a;if ((as = cells) != null || !casBase(b = base, b + x)) {boolean uncontended = true;if (as == null || (m = as.length - 1) < 0 ||(a = as[getProbe() & m]) == null ||!(uncontended = a.cas(v = a.value, v + x)))longAccumulate(x, null, uncontended);}
}
casBase
就是一个CAS操作(一般自旋锁这里是whille(!cas){}
),当有竞争的时候,CAS可能操作失败
java"> /*** CASes the base field.*/final boolean casBase(long cmp, long val) {return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);}
- as == null,
- (m = as.length - 1) < 0
- (a = as[getProbe() & m]) == null
- !(uncontended = a.cas(v = a.value, v + x))
- if中判断1和判断2是判断cells是否为空
- 判断3是判断当前线程的cell是否是null
- 判断4是当前线程进行cas操作
- 最后是
longAccumulate(x, null, uncontended);
longAccumulate(x, null, uncontended)
如果Cell[]
数组未初始化,会调用父类的longAccumelate去初始化Cell[]
,如果Cell[]
已经初始化但是冲突发生在Cell单元内,则也调用父类的longAccumelate,此时可能就需要对Cell[]
扩容了。
sum()
base + cells
数组中各内容值
java">public long sum() {Cell[] as = cells; Cell a;long sum = base;if (as != null) {for (int i = 0; i < as.length; ++i) {if ((a = as[i]) != null)sum += a.value;}}return sum;
}
LongAdder
和AtomicLong
性能对比