写点什么

Java 中的 13 个原子操作类

作者:周杰伦本人
  • 2022 年 6 月 05 日
  • 本文字数:2238 字

    阅读完需:约 7 分钟

Java 中的 13 个原子操作类

Atomic 包里一共提供了 13 个类,属于 4 种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。


Atomic 包里的类基本都是使用 Unsafe 实现的包装类。

原子更新基本类型类

  • AtomicBoolean:原子更新布尔类型。

  • AtomicInteger:原子更新整型。

  • AtomicLong:原子更新长整型。


import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {    static AtomicInteger ai = new AtomicInteger(1);    public static void main(String[] args) {        System.out.println(ai.getAndIncrement());        System.out.println(ai.get());    }}
复制代码


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


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;}
复制代码


源码中 for 循环体的第一步先取得 AtomicInteger 里存储的数值,第二步对 AtomicInteger 的当前数值进行加 1 操作,关键的第三步调用 compareAndSet 方法来进行原子更新操作,该方法先检查当前数值是否等于 current,等于意味着 AtomicInteger 的值没有被其他线程修改过,则将 AtomicInteger 的当前数值更新成 next 的值,如果不等 compareAndSet 方法会返回 false,程序会进入 for 循环重新进行 compareAndSet 操作。


Atomic 包提供了 3 种基本类型的原子更新,但是 Java 的基本类型里还有 char、float 和 double 等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic 包里的类基本都是使用 Unsafe 实现的


Unsafe 只提供了 3 种 CAS 方法:compareAndSwapObject、compareAndSwapInt 和 compareAndSwapLong,再看 AtomicBoolean 源码,发现它是先把 Boolean 转换成整型,再使用 compareAndSwapInt 进行 CAS,所以原子更新 char、float 和 double 变量也可以用类似的思路来实现。


public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object x);
public final native boolean compareAndSwapInt(Object o, long offset, int expected, int x);
public final native boolean compareAndSwapLong(Object o, long offset, long expected, long x);
复制代码

原子更新数组

通过原子的方式更新数组里的某个元素,Atomic 包提供了以下 3 个类。


  • AtomicIntegerArray:原子更新整型数组里的元素。

  • AtomicLongArray:原子更新长整型数组里的元素。

  • AtomicReferenceArray:原子更新引用类型数组里的元素。


public class AtomicIntegerArrayTest {    static int[] value = new int[]{1, 2};    static AtomicIntegerArray ai = new AtomicIntegerArray(value);
public static void main(String[] args) { ai.getAndSet(0, 3); System.out.println(ai.get(0)); System.out.println(value[0]); }}
复制代码


输出结果:


31
复制代码


需要注意的是,数组 value 通过构造方法传递进去,然后 AtomicIntegerArray 会将当前数组复制一份,所以当 AtomicIntegerArray 对内部的数组元素进行修改时,不会影响传入的数组。

原子更新引用类型

原子更新基本类型的 AtomicInteger,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic 包提供了以下 3 个类。


  • AtomicReference:原子更新引用类型。

  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。

  • AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和引用类型。构造方法是 AtomicMarkableReference(V initialRef,boolean initialMark)。


import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceTest { public static AtomicReference<User> atomicUserRef = new AtomicReference<User>();
public static void main(String[] args) { User user = new User("zhoujielun", 15); atomicUserRef.set(user); User updateUser = new User("wuyifan", 17); atomicUserRef.compareAndSet(user, updateUser); System.out.println(atomicUserRef.get().getName()); System.out.println(atomicUserRef.get().getOld()); }
static class User { private String name; private int old;
public User(String name, int old) { this.name = name; this.old = old; }
public String getName() { return name; }
public int getOld() { return old; } }}
复制代码


代码中首先构建一个 user 对象,然后把 user 对象设置进 AtomicReference 中,最后调用 compareAndSet 方法进行原子更新操作,实现原理同 AtomicInteger 里的 compareAndSet 方法

原子更新字段类

如果需原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic 包提供了以下 3 个类进行原子字段更新。


  • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。

  • AtomicLongFieldUpdater:原子更新长整型字段的更新器。

  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。

发布于: 刚刚阅读数: 3
用户头像

还未添加个人签名 2020.02.29 加入

还未添加个人简介

评论

发布
暂无评论
Java中的13个原子操作类_6月月更_周杰伦本人_InfoQ写作社区