字段更新器,主要是用来更新自定义类的字段的。Java 提供以下三种字段更新器:
public class Test5 {
private volatile int field;
public static void main(String[] args) {
AtomicIntegerFieldUpdater fieldUpdater =
AtomicIntegerFieldUpdater.newUpdater(Test5.class, "field");
Test5 test5 = new Test5();
fieldUpdater.compareAndSet(test5, 0, 10);
// 修改成功 field = 10
System.out.println(test5.field);
// 修改成功 field = 20
fieldUpdater.compareAndSet(test5, 10, 20);
System.out.println(test5.field);
// 修改失败 field = 20
fieldUpdater.compareAndSet(test5, 10, 30);
System.out.println(test5.field);
}
}
值得注意的是:字段更新器要操作哪个字段,哪个字段必须被 volatile 修饰。
原子累加器顾名思义就是用来计数的,Java 中可以使用 LongAdder 和 AtomicLong/AtomicInteger。
AtomicLong/AtomicInteger 的实现方式中,内部会维护一个 value 变量用来存放实际的值(自增、自减等都是操作这个变量),这就导致在多线程环境下,CAS 操作失败后会不断进行循环重试。也就是说,当并发量大的情况下,会出现很多次循环重试,从而降低了效率。
简单来说:AtomicLong/AtomicInteger 都通过 while 循环执行 CAS 操作的,所以说,当并发量大的情况下,会出现多次重试,从而降低效率。
而 LongAdder 是 Java 8 新增的用于并发环境的计数器,目的是为了在高并发情况下,代替 AtomicLong/AtomicInteger,成为一个用于高并发情况下的高效的通用计数器。
LongAdder 继承了 Striped64 类(支持高并发的累加器),来实现累加功能的;Striped64 的设计核心思路就是通过内部的分散计算来避免竞争。
Striped64 内部三个重要的成员变量:
/**
* 累加单元数组,懒惰初始化。
*/
transient volatile Cell[] cells;
/**
* 基础值,
* 1. 在没有竞争时会更新这个值;
* 2. 在 cells 初始化的过程中,cells 处于不可用的状态,这时候也会尝试将通过 cas 操作值累加到 base。
*/
transient volatile long base;
/**
* CAS 锁。在 Cell 扩容的时候,会设置为 1。
*/
transient volatile int cellsBusy;
LongAdder 的核心逻辑是在 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;
// 这里执行 as == null 是为了防止 as.length 出现空指针异常。
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);
}
}
下面是当前线程获取累加单元的逻辑:
static final int getProbe() {
return UNSAFE.getInt(Thread.currentThread(), PROBE);
}
该方法主要是用来获取当前线程的随机探针。
原文:https://www.cnblogs.com/daihao-g/p/15040425.html