JUC-Atomic 整数类型

Atomic 整数类型

常用API

常见原子类:AtomicInteger、AtomicBoolean、AtomicLong

构造方法:

  • public AtomicInteger():初始化一个默认值为 0 的原子型 Integer
  • public AtomicInteger(int initialValue):初始化一个指定值的原子型 Integer

常用API:

方法 作用
public final int get() 获取 AtomicInteger 的值
public final int getAndIncrement() 以原子方式将当前值加 1,返回的是自增前的值
public final int incrementAndGet() 以原子方式将当前值加 1,返回的是自增后的值
public final int getAndSet(int value) 以原子方式设置为 newValue 的值,返回旧值
public final int addAndGet(int data) 以原子方式将输入的数值与实例中的值相加并返回
实例:AtomicInteger 里的 value
public final int getAndUpdate(IntUnaryOperator updateFunction) 可以自定义操作逻辑
public final int updateAndGet(IntUnaryOperator updateFunction) 可以自定义操作逻辑

原理分析

AtomicInteger 原理:自旋锁 + CAS 算法

CAS 算法:有 3 个操作数(内存值 V, 旧的预期值 A,要修改的值 B)

  • 当旧的预期值 A == 内存值 V 此时可以修改,将 V 改为 B
  • 当旧的预期值 A != 内存值 V 此时不能修改,并重新获取现在的最新值,重新获取的动作就是自旋

分析 getAndSet 方法:

  • AtomicInteger:

    1
    2
    3
    4
    5
    6
    7
    public final int getAndSet(int newValue) {
    /**
    * this: 当前对象
    * valueOffset: 内存偏移量,内存地址
    */
    return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

    valueOffset:偏移量表示该变量值相对于当前对象地址的偏移,Unsafe 就是根据内存偏移地址获取数据

    1
    2
    3
    4
    valueOffset = unsafe.objectFieldOffset
    (AtomicInteger.class.getDeclaredField("value"));
    //调用本地方法 -->
    public native long objectFieldOffset(Field var1);
  • unsafe 类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // val1: AtomicInteger对象本身,var2: 该对象值得引用地址,var4: 需要变动的数
    public final int getAndSetInt(Object var1, long var2, int var4) {
    int var5;
    do {
    // var5: 用 var1 和 var2 找到的内存中的真实值
    var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var4));

    return var5;
    }

    var5:从主内存中拷贝到工作内存中的值(每次都要从主内存拿到最新的值到本地内存),然后执行 compareAndSwapInt() 再和主内存的值进行比较,假设方法返回 false,那么就一直执行 while 方法,直到期望的值和真实值一样,修改数据

  • 变量 value 用 volatile 修饰,保证了多线程之间的内存可见性,避免线程从工作缓存中获取失效的变量

    1
    private volatile int value

    CAS 必须借助 volatile 才能读取到共享变量的最新值来实现比较并交换的效果

分析 getAndUpdate 方法:

  • getAndUpdate:

    1
    2
    3
    4
    5
    6
    7
    8
    public final int getAndUpdate(IntUnaryOperator updateFunction) {
    int prev, next;
    do {
    prev = get(); //当前值,cas的期望值
    next = updateFunction.applyAsInt(prev);//期望值更新到该值
    } while (!compareAndSet(prev, next));//自旋
    return prev;
    }

    函数式接口:可以自定义操作逻辑

    1
    2
    AtomicInteger a = new AtomicInteger();
    a.getAndUpdate(i -> i + 10);
  • compareAndSet:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public final boolean compareAndSet(int expect, int update) {
    /**
    * this: 当前对象
    * valueOffset: 内存偏移量,内存地址
    * expect: 期望的值
    * update: 更新的值
    */
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
文章作者: GeYu
文章链接: https://nuistgy.github.io/2023/04/23/JUC-Atomic/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yu's Blog