Atomic 更新操作使用案例以及实现原理

上文中介绍了原子化更新的必要性以及其基本实现原理,是使用Unsafe的API记性操作,UNsafe的API 主要是Native级别 的方法,因此,AtomicXXX 基本都是Unsafe类的封装,本篇文章笔者将从实际的类的源码中分析原子类操作的实现方式。

1、更新基本类型


使用原子类更新基本类型JUC 提供了三种类: AtomicBoolean,AtomicInteger以及AtomicLong。由于这三种基本类型的原理基本类似,笔者这里主要通过AtomicInteger 来分析对于基本类型的原子更新原理。


AtomicInteger 类主要有以下API用于操作:

// 1.以原子的方式将实例的值与给定的值#{delta} 相加并返回新值
int addAndGet(int delta);

// 2. 如果输入的值等于预期值#{expect},那么将值更新为 #{update}
boolean compareAndSet(int expect,int update){}; 

// 3. 以原子的方式设置实例的值为#{newValue} 并返回旧的值
int getAndSet(int newValue);

// 4. 以原子的方式自增1,并返回旧值
int getAndIncrement(();


比如下面的代码示例中就演示部分API 的使用

AtomicInteger integer = new AtomicInteger(1);

System.out.println(integer.getAndIncrement());
System.out.println(integer.get());


// 输出结果 1,2


那么 getAndIncrement() 是怎么实现的呢?


  public final int getAndIncrement() {
    return U.getAndAddInt(this, VALUE, 1);
  }

  @HotSpotIntrinsicCandidate
  public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
      v = this.getIntVolatile(o, offset);
    } while(!this.weakCompareAndSetInt(o, offset, v, v + delta));

    return v;
  }


经过浏览源码可知:

  1. 通过 v = this.getIntVolatile(o, offset); 获取到原来的值
  2. 通过 weakCompareAndSetInt 尝试设置将新增值加1 ,如果CAS 设置失败,那么返回第1步
  3. 如果设置成功,返回原来的v值


虽然Atomic只提供了三种操作类型: Boolean,Integer以及Long,其实其他的类型也是类似的,比如AtomicBoolean的实现就是将布尔类型转换为Integer,实际上你也可以自己实现AtomicChar 或者 AtomicShort类型。

2、更新数组类型

使用原子类更新数组主要有三种实现方式: AtomicIntegerArray, AtomicLongArray以及AtomicRefrenceArray。这里以AtomicIntegerArray 做一些简单的分析。

  public static void main(String[] args) {
    int[] intArray = new int[]{1,2,3,4};
    AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(intArray);
    atomicIntegerArray.getAndSet(0,0);
    atomicIntegerArray.getAndSet(1,1);
    atomicIntegerArray.getAndSet(2,2);
    atomicIntegerArray.getAndSet(3,3);
  }

> 需要注意的是,在构造 AtomicIntegerArray 的时候,其内部的数据是传递参数的一份拷贝,并非直接引用,所以在修改 atomicIntegerArray 的时候并不会对 intArray 有影响.

3、更新引用类型

原子更新基本类型,只能更新一个变量,比如一个整型Integer 或者布尔类型Boolean。如果需要原子更新多个引用变量,就会用到更新引用类型的 AtomicRefrence.

 public static void main(String[] args) {
    AtomicReference<User> atomicReference = new AtomicReference<>();
    User oldUser = new User("张三", 12);
    atomicReference.set(oldUser);

    User newUser = new User("李四", 18);
    atomicReference.compareAndSet(oldUser, newUser);

    System.out.println("value = " + atomicReference.get());
  }

  @Data
  @AllArgsConstructor
  static class User {
    String name;
    Integer age;
  }

4、原子更新字段类型

需要原子化更新某个类的某个字段的时候,则需要使用原子更新字段类。

  • AtomicIntegerFieldUpdater 原子更新Integer的字段
  • AtomicLongFieldUpdater 原子更新Long的字段
  • AtomicStampedRefrencr 原子更新带版本号的引用类型


想要原子地更新某个字段,需要两个过程:

  1. 因为原子更新的字段都属于抽象类,所以在更新之前都必须使用 newUpdater 构建一个更新器
  2. 更新的字段类型必须为 public volatile 修饰


比如,我们想要更新User的年龄

  public static void main(String[] args) {
    // 构建更新器
    AtomicIntegerFieldUpdater<User> updated =
        AtomicIntegerFieldUpdater.newUpdater(User.class, "age");

    User user = new User("张三", 12);
    System.out.println(updated.incrementAndGet(user));
  }

  @Data
  @AllArgsConstructor
  static class User {
    String name;

    // 必须是public volatile 修饰
    public volatile int age;
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值