Java原子操作类


image-20200821151856029

java.util.concurrent.atomic包提供了多类用法简单、性能高效、线程安全的原子操作类。主要包含以下四种类型:

  • 原子更新基本类型
  • 原子更新数组
  • 原子更新引用
  • 原子更新属性(字段)

1. 原子更新基本类型

其中原子更新基本类型主要是如下三个类:

  • AtomicBoolean:原子更新布尔类型
  • AtomicInteger:原子更新整型
  • AtomicLong:原子更新长整型

其中AtomicInteger的源码定义如下:

public class AtomicInteger extends Number implements Serializable {
    private static final long serialVersionUID = 6214790243416807050L;
    private static final Unsafe unsafe = Unsafe.getUnsafe(); // 底层实现依赖于Unsafe类
    private static final long valueOffset;  // 偏移量
    private volatile int value;  // 值

    public AtomicInteger(int var1) {
        this.value = var1;
    }

    public AtomicInteger() {
    }
}

其中包含的方法如下:


image-20200821152122427

我们着重看几个比较常用的方法,它们当然都是以原子的方式执行:

  • int addAndGet(int delta):将传入的值和实例中的value相加并返回
  • boolean compareAndSet(int expect, int update):如果输入的值等于预期值,则将该值设置为输入的值
  • int getAndIncrement():自增,返回自增前的值
  • void lazySet(int newValue):懒设置的方式将值最后设置为newValue
  • int getAndSet(int newValue):将实例中的value设置为newValue,并返回旧值

测试代码:

@Test
public void testAtomicInteger() throws NoSuchFieldException, IllegalAccessException {
    AtomicInteger ai = new AtomicInteger(1);

    Field value = ai.getClass().getDeclaredField("value");
    System.out.println(value.toString()); // private volatile int java.util.concurrent.atomic.AtomicInteger.value

    System.out.println(ai.incrementAndGet());  // 2

    System.out.println(ai.getAndIncrement());  // 2
    System.out.println(ai.get());  // 3

    System.out.println(ai.decrementAndGet());  // 2
    System.out.println(ai.get());  // 2

    System.out.println(ai.compareAndSet(2, 5));  // true
    System.out.println(ai.get());                               // 5
    System.out.println(ai.compareAndSet(3, 10)); // false
}

类中的方法使用并不难,我们再看一下方法的实现源码。首先看一下getAndIncrement()的源码,如下所示:

public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

它调用的是Unsafe类中的getAndAddInt()来实现真正的逻辑,它的源码如下:

/**
* var1:操作的对象
* var2:对象中字段的偏移量
* var4:要增加的值
*/
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        // 首先获取当前的值
        var5 = this.getIntVolatile(var1, var2);
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); // 试更新对应偏移量处的值

    return var5;
}

while循环中使用compareAndSwapInt来保证原子自增,本质上使用的还是CAS操作,如果当前值和主内存中的值一样 ,说明当前值没有被其他线程改变,直接返回当前值;否则,需要按照偏移量刷新工作内存中的值,然后再返回最新值。

其中的compareAndSwapInt()为一个本地方法,源码定义如下:

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

2. 原子更新数组

原子更新数组指的是以原子的方式更新数组中的某个索引位置的元素,主要包括以下4个类:

  • AtomicIntegerArray:更新整型数组中的元素
  • AtomicLongArray:更新长整型数组中的元素
  • AtomicReferenceArray:更新引用类型数组中的元素

其中同样以AtomicIntegerArray为例说明,它的源码定义如下:

public class AtomicIntegerArray implements Serializable {
    private static final long serialVersionUID = 2862133569453604235L;
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final int base;
    private static final int shift;
    private final int[] array;  // 内部定义了一个数组
    
    // 如果传入的是数组大小,则array初始化为指定大小的int型数组
    public AtomicIntegerArray(int var1) {
        this.array = new int[var1];
    }
	// 如果传入的是已有的数组,则array为传入数组的副本
    public AtomicIntegerArray(int[] var1) {
        this.array = (int[])var1.clone();
    }
}

其中常用的方法有:

  • int addAndGet(int i, int delta):将输入值和对应索引i位置的元素相加
  • boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则将数组索引为i位置的元素设置为update值

测试代码:

@Test
public void testAtomicIntegerArray(){
    AtomicIntegerArray array = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5});

    array.getAndSet(0, 10);
    System.out.println(array.toString());  // [10, 2, 3, 4, 5]

    System.out.println(array.get(2)); // 3

    System.out.println(array.compareAndSet(0, 11, 24)); // false
    System.out.println(array.compareAndSet(0, 10, 24));  // true
    System.out.println(array.toString());  // [24, 2, 3, 4, 5]

}

下面看一下compareAndSet()的源码实现,它的源码如下:

public final boolean compareAndSet(int i, int expect, int update) {
    return compareAndSetRaw(checkedByteOffset(i), expect, update);
}

private boolean compareAndSetRaw(long offset, int expect, int update) {
    return unsafe.compareAndSwapInt(array, offset, expect, update);
}

最终实现上还是调用了Unsafe的compareAndSwapInt()

 public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

3. 原子更新引用类型

原子更新引用类型包括如下三个类:

  • AtomicReference:更新引用类型
  • AtomicReferenceFieldUpdate :更新引用类型中的字段
  • AtomicMarkableReference:更新带有标记位的引用类型,可以更新一个布尔类型的标记位和引用类型

测试代码:

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Person{
    private String name;
    public volatile int age;
}

@Test
public void testAtomicReference() {
    AtomicReference<Person> atomicReference = new AtomicReference<>();
    Person person = new Person().builder().name("Forlogen").age(10).build();
    System.out.println(person);  // Person(name=Forlogen, age=10)
    atomicReference.set(person);

    Person person1 = new Person().builder().name("Kobe").age(24).build();
    atomicReference.compareAndSet(person, person1);   // 更新
    System.out.println(atomicReference.get().getAge() + " " + atomicReference.get().getName());  // 24 Kobe
}

4. 原子更新字段类

原子更新字段类自然是用于更新某个类中的字段值,主要包含如下三个类:

  • AtomicIntegerFieldUpdater:更新整型字段的更新器
  • AtomicLongFieldUpdater:更新长整型字段的更新器
  • AtomicStampedReference:更新带有版本号的引用类型,需要更新版本号和引用类型,主要为了解决ABA问题

使用步骤为:

  1. 使用静态方法newUpdater创建更新器,设置要更新的类和属性
  2. 更新类中使用public volatile修饰的字段

测试代码:

@Test
public void testAtomicIntegerFieldUpdater() {
    AtomicIntegerFieldUpdater<Person> updater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age");

    Person person = new Person().builder().name("Forlogen").age(10).build();
    System.out.println(person);  // Person(name=Forlogen, age=10)

    System.out.println(updater.getAndIncrement(person));  // 10
    System.out.println(updater.get(person));  // 11

}
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Person{
    private String name;
    public volatile int age;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值