6.原子操作类

原子操作类

总览

分类

基本类型原子类

  • AtomicBoolean

  • AtomicInteger

  • AtomicLong

常见API

AtomicBoolean

/**
* Returns the current value.
*
* @return the current value
*/
public final boolean get() {
return value != 0;
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* <p><a href="package-summary.html#weakCompareAndSet">May fail
* spuriously and does not provide ordering guarantees</a>, so is
* only rarely an appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful
*/
public boolean weakCompareAndSet(boolean expect, boolean update) {
int e = expect ? 1 : 0;
int u = update ? 1 : 0;
return unsafe.compareAndSwapInt(this, valueOffset, e, u);
}
/**
* Unconditionally sets to the given value.
*
* @param newValue the new value
*/
public final void set(boolean newValue) {
value = newValue ? 1 : 0;
}
/**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(boolean newValue) {
int v = newValue ? 1 : 0;
unsafe.putOrderedInt(this, valueOffset, v);
}
/**
* Atomically sets to the given value and returns the previous value.
*
* @param newValue the new value
* @return the previous value
*/
public final boolean getAndSet(boolean newValue) {
boolean prev;
do {
prev = get();
} while (!compareAndSet(prev, newValue));
return prev;
}
/**
* Returns the String representation of the current value.
* @return the String representation of the current value
*/
public String toString() {
return Boolean.toString(get());
}

AtomicInteger

/**
* Gets the current value.
*
* @return the current value
*/
public final int get() {
return value;
}
/**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
/**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
/**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

AtomicInteger

/**
* Gets the current value.
*
* @return the current value
*/
public final long get() {
return value;
}
/**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(long newValue) {
value = newValue;
}
/**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final long getAndSet(long newValue) {
return unsafe.getAndSetLong(this, valueOffset, newValue);
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
}
/**
* Atomically decrements by one the current value.
*
* @return the previous value
*/
public final long getAndDecrement() {
return unsafe.getAndAddLong(this, valueOffset, -1L);
}
/**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final long getAndAdd(long delta) {
return unsafe.getAndAddLong(this, valueOffset, delta);
}

数组类型原子类

AtomicIntegerArray

// 构造方法
/**
* Creates a new AtomicIntegerArray of the given length, with all
* elements initially zero.
*
* @param length the length of the array
*/
public AtomicIntegerArray(int length) {
array = new int[length];
}
/**
* Creates a new AtomicIntegerArray with the same length as, and
* all elements copied from, the given array.
*
* @param array the array to copy elements from
* @throws NullPointerException if array is null
*/
public AtomicIntegerArray(int[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}

AtomicReferenceArray

// 构造方法
/**
* Creates a new AtomicReferenceArray of the given length, with all
* elements initially null.
*
* @param length the length of the array
*/
public AtomicReferenceArray(int length) {
array = new Object[length];
}
/**
* Creates a new AtomicReferenceArray with the same length as, and
* all elements copied from, the given array.
*
* @param array the array to copy elements from
* @throws NullPointerException if array is null
*/
public AtomicReferenceArray(E[] array) {
// Visibility guaranteed by final field guarantees
this.array = Arrays.copyOf(array, array.length, Object[].class);
}

AtomicLongArray

//构造方法
/**
* Creates a new AtomicLongArray of the given length, with all
* elements initially zero.
*
* @param length the length of the array
*/
public AtomicLongArray(int length) {
array = new long[length];
}
/**
* Creates a new AtomicLongArray with the same length as, and
* all elements copied from, the given array.
*
* @param array the array to copy elements from
* @throws NullPointerException if array is null
*/
public AtomicLongArray(long[] array) {
// Visibility guaranteed by final field guarantees
this.array = array.clone();
}

引用类型原子类

  • AtomicReference

自旋锁

  • AtomicStampedReference

携带版本号的引用类型原子类,每次改动版本号都会发生变化,可以解决ABA问题

  • AtomicMarkableReference

原子更新带有标记位的引用类型对象

解决是否修改过,他的定义就是将状态简化为true|false

static AtomicMarkableReference atomicMarkableReference = new AtomicMarkableReference(100, false);
public static void main(String[] args) {
new Thread(() -> {
boolean marked = atomicMarkableReference.isMarked();
System.out.println("t1--------------默认标识 " + marked);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
atomicMarkableReference.compareAndSet(100, 1000, marked, !marked);
}, "t1").start();
new Thread(() -> {
boolean marked = atomicMarkableReference.isMarked();
System.out.println("t2-------------默认标识 " + marked);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
boolean b = atomicMarkableReference.compareAndSet(100, 1000, marked, !marked);
System.out.println("t2 compareAndSet " + b);
System.out.println("atomicMarkableReference.isMarked() " + atomicMarkableReference.isMarked());
System.out.println("atomicMarkableReference.getReference() " + atomicMarkableReference.getReference());
}, "t2").start();
}
执行结果:
t1--------------默认标识 false
t2-------------默认标识 false
t2 compareAndSet false
atomicMarkableReference.isMarked() true
atomicMarkableReference.getReference() 1000

对象的属性修改原子类

  • AtomicIntegerFieldUpdater

    原子更新对象中int类型字段的值

  • AtomicReferenceFieldUpdater

    原子更新对象中引用类型字段的值

  • AtomicLongFieldUpdater

    原子更新对象中long类型字段的值

作用

以一种线程安全的方式操作非线程安全对象内的某个字段

使用要求

  1. 更新的对象属性必须用public volatile修饰
  2. 因为对象的属性修改类型原子类都是抽象类所以每次都必须使用静态方法newUpdater创建一个更新器,并且需要设置想要更新的类和属性
// synchronized 修饰
public class AtomicIntegerFieldUpdaterDemo {
public static void main(String[] args) throws InterruptedException {
Bank bank = new Bank();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 1000; j++) {
bank.add();
}
} finally {
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
System.out.println(bank.money);
}
}
class Bank {
String name = "BBB";
int money = 0;
public synchronized void add() {
money++;
}
}
结果:
10000
不使用 synchronized 修饰,使用对象属性修改原子类
public class AtomicIntegerFieldUpdaterDemo {
public static void main(String[] args) throws InterruptedException {
Bank bank = new Bank();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 1000; j++) {
bank.transMoney(bank);
}
} finally {
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
System.out.println(bank.money);
}
}
class Bank {
String name = "BBB";
// 更新的对象属性必须用public volatile修饰
public volatile int money = 0;
public void add() {
money++;
}
// 因为对象的属性修改类型原子类都是抽象类所以每次都必须使用静态方法newUpdater创建一个更新器,并且需要设置想要更新的类和属性
AtomicIntegerFieldUpdater<Bank> fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Bank.class, "money");
public void transMoney (Bank bank){
fieldUpdater.getAndIncrement(bank);
}
}

AtomicReferenceFieldUpdater使用

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class AtomicReferenceFieldUpdaterDemo {
public static void main(String[] args) {
My my = new My();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
my.init(my);
}, String.valueOf(i)).start();
}
}
}
class My {
public volatile Boolean isInit = Boolean.FALSE;
AtomicReferenceFieldUpdater<My, Boolean> updater = AtomicReferenceFieldUpdater
.newUpdater(My.class, Boolean.class, "isInit");
public void init(My my) {
if (updater.compareAndSet(my, Boolean.FALSE, Boolean.TRUE)) {
System.out.println(Thread.currentThread().getName() + "\t" + "start init need 2 seconds");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "\t" + "over init ");
} else {
System.out.println(Thread.currentThread().getName()+"已经有线程在做初始化工作。。。。。。。。");
}
}
}
结果:
0 start init need 2 seconds
4已经有线程在做初始化工作。。。。。。。。
3已经有线程在做初始化工作。。。。。。。。
2已经有线程在做初始化工作。。。。。。。。
1已经有线程在做初始化工作。。。。。。。。
0 over init

原子操作增强

列表

  • DoubleAccumulator

    One or more variables that together maintain a running double value updated using a supplied function.

  • DoubleAdder

    One or more variables that together maintain an initially zero double sum.

  • LongAccumulator

    One or more variables that together maintain a running long value updated using a supplied function.

  • LongAdder

    One or more variables that together maintain an initially zero long sum.

LongAdder只能计算加法,且从零开始计算

LongAccumulator提供了自定义函数计算

LongAdder主要方法

/**
* Creates a new adder with initial sum of zero.
*/
public LongAdder() {
}
// 添加给定值
public void add(long x) {}
// 自增
public void increment() {}
// 自减
public void decrement() {}
// 返回当前和。返回值不是原子快照;在没有并发更新的情况下调用将返回准确的结果,但是在计算总和时发生的并发更新可能不会被合并。
// 没有并发是准确值,并发的时候不是准确值
public long sum() {}
// 将value设置为0L,可用于替代重新new一个LongAdder,只能在没有并发更新的时候使用
public void reset() {}
// 获取当前值然后重置为0L
public long sumThenReset() {}
/**
* Creates a new instance using the given accumulator function
* and identity element.
* @param accumulatorFunction a side-effect-free function of two arguments
* @param identity identity (initial value) for the accumulator function
*/
public DoubleAccumulator(DoubleBinaryOperator accumulatorFunction,
double identity) {
this.function = accumulatorFunction;
base = this.identity = Double.doubleToRawLongBits(identity);
}
@FunctionalInterface
public interface DoubleBinaryOperator {
/**
* Applies this operator to the given operands.
*
* @param left the first operand
* @param right the second operand
* @return the operator result
*/
double applyAsDouble(double left, double right);
}
// left 是和,right是要添加的值
DoubleAccumulator d = new DoubleAccumulator((left, right) -> left + right, 2);
d.accumulate(1);

性能比较

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
public class Click {
int number = 0;
public synchronized void clickBySync() {
number++;
}
AtomicLong atomicLong = new AtomicLong();
public void clickByAtomicLong() {
atomicLong.getAndIncrement();
}
LongAdder atomicLongAdder = new LongAdder();
public void clickByLongAdder() {
atomicLongAdder.increment();
}
LongAccumulator atomicLongAccumulator = new LongAccumulator((left, right) -> left + right, 0);
public void clickByLongAccumulator() {
atomicLongAccumulator.accumulate(1L);
}
}
class Calculation {
public static final int _1W = 1000000;
public static final int THREAD_NUMBER = 50;
public static void main(String[] args) throws InterruptedException {
Click click = new Click();
long start;
long end;
CountDownLatch latch = new CountDownLatch(THREAD_NUMBER);
CountDownLatch latch1 = new CountDownLatch(THREAD_NUMBER);
CountDownLatch latch2 = new CountDownLatch(THREAD_NUMBER);
CountDownLatch latch3 = new CountDownLatch(THREAD_NUMBER);
start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUMBER; i++) {
new Thread(() -> {
for (int j = 0; j < _1W; j++) {
click.clickBySync();
}
latch.countDown();
}).start();
}
latch.await();
end = System.currentTimeMillis();
System.out.println("clickBySync cost time -" + (end - start) + "ms" + "\t" + click.number);
start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUMBER; i++) {
new Thread(() -> {
for (int j = 0; j < _1W; j++) {
click.clickByAtomicLong();
}
latch1.countDown();
}).start();
}
latch1.await();
end = System.currentTimeMillis();
System.out.println("clickByAtomicLong cost time -" + (end - start) + "ms" + "\t" + click.atomicLong.get());
start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUMBER; i++) {
new Thread(() -> {
for (int j = 0; j < _1W; j++) {
click.clickByLongAdder();
}
latch2.countDown();
}).start();
}
latch2.await();
end = System.currentTimeMillis();
System.out.println("atomicLongAdder cost time -" + (end - start) + "ms" + "\t" + click.atomicLongAdder.sum());
start = System.currentTimeMillis();
for (int i = 0; i < THREAD_NUMBER; i++) {
new Thread(() -> {
for (int j = 0; j < _1W; j++) {
click.clickByLongAccumulator();
}
latch3.countDown();
}).start();
}
latch3.await();
end = System.currentTimeMillis();
System.out.println("clickByLongAccumulator cost time -" + (end - start) + "ms" + "\t" + click.atomicLongAccumulator.get());
}
}
结果:
clickBySync cost time -7546ms 50000000
clickByAtomicLong cost time -1749ms 50000000
atomicLongAdder cost time -170ms 50000000
atomicLongAdder cost time -159ms 50000000
高并发的情况下 atomicLongAdder atomicLongAdder 的性能更好

LongAdder 增强类性能源码分析

add()
  1. LongAdder 架构

    1687743248227

  2. Striped64 重要成员函数

/** Number of CPUS, to place bound on table size */
// cpu数量 cell数组的最大长度
static final int NCPU = Runtime.getRuntime().availableProcessors();
/**
* Table of cells. When non-null, size is a power of 2.
*/
transient volatile Cell[] cells;
/**
* Base value, used mainly when there is no contention, but also as
* a fallback during table initialization races. Updated via CAS.
*/
// 基础value值,当并发比较低的时候只累加该值,用于没有竞争的情况,通过cas更新
transient volatile long base;
/**
* Spinlock (locked via CAS) used when resizing and/or creating Cells.
*/
// 创建或者扩容cells数组时使用的自旋变量调整单元格大小(扩容),创建单元格时使用的锁
transient volatile int cellsBusy;
  1. Cell

    是java.util.concurrent.atomic 下 Striped64的一个内部类

  2. 为什么LongAdder 在高并发的情况下运行速度快

    ​ LongAdder的基本思路就是分散热点,将value值分散到一个Cell数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。

    ​ sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点。

    ​ 总结:LongAdder 内部有一个base变量,一个Cell[]数组,低并发时,直接累加到该变量上,高并发时累加进各个线程自己的Cell[i] 中

  3. 源码解析

    /**
    * as: 标识cell引用
    * b:获取的base值
    * v:期望值
    * m:cells数组的长度
    * a:当前线程命中的cell单元格
    */
    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);
    }
    }
    /**
    * CASes the base field.
    */
    final boolean casBase(long cmp, long val) {
    // 参数:this(要更新的对象), BASE(valueOffset地址偏移量), cmp(expect), val(update)
    return UNSAFE.compareAndSwapLong(this, BASE, cmp, val);
    }
    /**
    * 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
    *
    *base:类似于AtomicLong中全局的value值。在没有竟争情况下数据直接累加到base上,或者cells扩容时,也需要将数据写入到base上
    *collide:表示扩容意向,false一定不会扩容,true可能会扩容。
    *cellsBusy:初始化cells或者扩容cells需要获取锁,0:表示无锁状态1:表示其他线程已经持有了锁
    *casCellsBusy();通过CAS操作修改cellsBusy的值,CAS成功代表获取锁,返回true
    *NCPU:当前计算机CPU数量,CelI数组扩容时会使用到getProbe():获取当前线程的hash值
    *advanceProbe():重置当前线程的hash值
    *
    */
    final void longAccumulate(long x, LongBinaryOperator fn,
    boolean wasUncontended) {
    // 存储线程的hash值
    int h;
    // 如果 getProbe() 为0 则说明,随机数未初始化
    if ((h = getProbe()) == 0) {
    // 使用 ThreadLocalRandom.current(); 为当前线程重新计算一个hash值强制初始化
    ThreadLocalRandom.current(); // force initialization
    // 重新获取hash值,hash值被重置就像一个新的线程一样,所以设置 wasUncontended = true ,重新计算hash值后认为此次不算一次竞争。
    h = getProbe();
    wasUncontended = true;
    }
    boolean collide = false; // True if last slot nonempty
    for (;;) {
    Cell[] as; Cell a; int n; long v;
    // cells已经初始化了
    if ((as = cells) != null && (n = as.length) > 0) {
    // 当线程的hash值运算后映射到的cell单元为null说明cell 没有被使用
    if ((a = as[(n - 1) & h]) == null) {
    if (cellsBusy == 0) { // Try to attach new Cell
    Cell r = new Cell(x); // Optimistically create
    // cellsBusy == 0 表示无锁状态, casCellsBusy() 设置cellsBusy为1 占有锁
    if (cellsBusy == 0 && casCellsBusy()) {
    boolean created = false;
    try { // Recheck under lock
    // 将新创建的 new Cell(x); 加入到 Cell[j] 中
    Cell[] rs; int m, 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
    }
    }
    collide = false;
    }
    // wasUncontended表示cell初始化后,当前线程竞争修改失败 wasUncontended = false, 这里只是重新设置这个值为true,紧接着执行 h = advanceProbe(h); 重新修改hash值,重新循环
    else if (!wasUncontended) // CAS already known to fail
    wasUncontended = true; // Continue after rehash
    // 说明当前线程对应的数组中有了数据,也重置过hash值,此时通过 cas尝试对当前数进行累加x操作,x默认为1,如果cas成功则直接跳出循环
    else if (a.cas(v = a.value, ((fn == null) ? v + x :
    fn.applyAsLong(v, x))))
    break;
    // 如果n大于cpu最大数量,不可扩容,,并通过 h = advanceProbe(h);进行修改hash值
    else if (n >= NCPU || cells != as)
    collide = false; // At max size or stale
    // 如果 collide 为false则修改为true然后重新计算当前线程的hash值,如果当前数组的长度已经大于cpu核数则回再次设置扩容意向collide为false
    else if (!collide)
    collide = true;
    // 如果当前是无锁状态,则进行cas获取锁
    else if (cellsBusy == 0 && casCellsBusy()) {
    try {
    // 当前cells数组和最先赋值的as是同一个数组,代表没有其他线程修改过
    if (cells == as) { // Expand table unless stale
    Cell[] rs = new Cell[n << 1]; // 左移一位 相当于*2
    for (int i = 0; i < n; ++i)
    rs[i] = as[i]; // 将之前的数组元素拷贝到新的数组中
    cells = rs; // 修改cells 引用
    }
    } finally {
    cellsBusy = 0; // 释放锁设置cellsBusy= 0
    }
    collide = false; // 设置扩容状态,继续执行循环
    continue; // Retry with expanded table
    }
    h = advanceProbe(h);
    }
    // cells 没有加锁且没有初始化,则尝试对他进行加锁,并初始化cell数组
    else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
    boolean init = false;
    try { // Initialize table
    if (cells == as) {
    //如果上面条件都执行成功就会执行数组的初始化及赋值操作,Cellrs = new Cel[2]表示数组的长度为2,rs[h &1]=new Cell(x)表示创建一个新的Cell元素,value是x值,默认为1。h&1类似于我们之前HashMap常用到的计算散列桶index的算法,通常都是hash &(table.len -1)。同hashmap一个意思。
    Cell[] rs = new Cell[2];
    rs[h & 1] = new Cell(x);
    cells = rs;
    init = true;
    }
    } finally {
    cellsBusy = 0;
    }
    if (init)
    break;
    }
    // cells 正在进行初始化则尝试直接在base上进行累加操作
    else if (casBase(v = base, ((fn == null) ? v + x :
    fn.applyAsLong(v, x))))
    break; // Fall back on using base
    }
    }
    流程:
    1. as数组未初始化时,!casBase(b = base, b + x) 对base进行cas,如果cas成功直接在base上进行操作
    2. as数组未初始化时,!casBase(b = base, b + x) base进行cas,如果cas失败,进入if方法体
    uncontended = true 默认没有冲突,as == null 结果为true 进入 longAccumulate(x, null, uncontended); 方法 尝试对cells进行初始化
    3. cell数组初始化 将 new Cell[2]; 设置Cell[]数组大小为2
    4.初始化完成后,(as = cells) != nulltrue,as == null || (m = as.length - 1) < 0false , (a = as[getProbe() & m]) == null 如果为false 则说明cell数组还有剩余空间, 然后对a(Cell)进行cas !(uncontended = a.cas(v = a.value, v + x)) 如果cas失败了则返回true,进行Cell[] 进行扩容
    5. 扩容:
    1.当线程的hash值运算后映射到的cell单元为null说明cell没有被使用直接扩容
    判断是无锁状态,如果是则创建新的cell然后再进行判断是否为无锁状态,如果是则进行cas持有锁,然后进行判断 如果 cells,不为空且 cells.length>0 且 cells对应槽位为空 则将这次新建的cell进行添加,跳出循环
    2.cells的当前槽位有值
    1.wasUncontended表示cell初始化后,当前线程竞争修改失败 wasUncontended = false
    然后进行设置 wasUncontended = true 表示没有竞争再进行重新设置 hash值 h = advanceProbe(h); 再进入竞争
    2.当当前线程对应的数组中有了数据,也重置过hash值,此时通过 cas尝试对当前数进行累加x操作,x默认为1,如果cas成功则直接跳出循环
    3.如果n大于cpu最大数量,不可扩容,,并通过 h = advanceProbe(h);进行修改hash值
    4. 如果 collide(扩容意向,false一定不会扩容,true可能会扩容。collide 初始值就是false) 为false则修改为true然后重新计算当前线程的hash值,如果当前数组的长度已经大于cpu核数则回再次设置扩容意向collide为false
    5.判断锁状态然后持有锁 cellsBusy == 0 && casCellsBusy() 再进行判断 cells == as cells于最初的as是不是同一个数组(检测是不是被别的线程修改过了) 判断成功则进行 扩容为原来的2倍 Cell[] rs = new Cell[n << 1]; 将原来的数组中的元素复制到新的数组中。
    6.如果有线程正在初始化,则其他线程进入兜底的判断里 else if (casBase(v = base, ((fn == null) ? v + x : fn.applyAsLong(v, x)))) 对base槽进行自旋
sum()
/**
* Returns the current sum. The returned value is <em>NOT</em> an
* atomic snapshot; invocation in the absence of concurrent
* updates returns an accurate result, but concurrent updates that
* occur while the sum is being calculated might not be
* incorporated.
*
* @return the sum
*/
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;
}
如果Cell[] as 为空 则对base进行累加
否则 对base和 Cell[] as 中的所有槽进行累加

为什么在并发情况下sum的值不精确?

​ 首先,最终返回的sum局部变量,初始被复制为base,而最终返回时,很可能base已经被更新了,而此时局部变量sum不会更新,造成不一致。

​ 其次,这里对cell的读取也无法保证是最后一次写入的值。所以,sum方法在没有并发的情况下,可以获得正确的结果。

总结

AtomicLong

​ 原理:

​ CAS+自旋
​ incrementAndGet

​ 场景:

​ 低并发下的全局计算
​ AtomicLong能保证并发情况下计数的准确性,其内部通过CAS来解决并发安全性的问题

​ 缺陷:

​ 高并发后性能急剧下降,因为AtomicLong的自旋会成为瓶颈

LongAdder

​ 原理:

​ CAS+Base+Cell数组分散

​ 空间换时间并分散了热点数据

​ 场景
​ 高并发下的全局计算
​ 缺陷
​ sum求和后还有计算线程修改结果的话,最后结果不够准确

posted @   奶油炒白菜  阅读(6)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示