Java中的原子操作类

概述

java.util.concurrent.atomic包一共提供了13个类,属于4中类型的原子更新方式:原子更新基本数据类型、原子更新数组、原子更新引用、原子更新属性。

原子更新基本类型

>java.util.concurrent.atomic包提供了以下3个类:

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

以上三个类提供的方法几乎一样,下面只分析AtomicInteger:

  • int addAndGet(int delta):以原子方式将输入的数值与实例中的数值(value)相加,并返回结果
  • boolean compareAndSet(int expect,int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值
  • int getAndIncrement():以原子方式将当前值加1,但是,返回的是自增前的值

  • void lazySet(int newValue):最终会设置成newValue,使用lazySet设置值后,可能导致其线程在之后的一小段时间内还是可以读到旧的值。

  • int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值。

通过getAndIncrement方法来看看实现原理:

    public final int getAndIncrement() {
        for (;;) {
            int current = get();//先取得AtomicInteger存储的数组
            int next = current + 1;//对当前数组加1
            if (compareAndSet(current, next))//CAS操作更新,先检查当前数值是否等于current,如果是则将AtomicInteger的当前数组更新成next;如果不是,则返回false,重新循环更新
                return current;//返回更新前的值
        }
    }
public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

如何原子的更新其他的基本类型?
由于java.util.concurrent.atomic包的类都是使用Unsafe实现的,先看一下Unsafe的源码:

 public native boolean compareAndSwapObject(Object obj, long offset,Object expect, Object update);

public native boolean compareAndSwapLong(Object obj, long offset,long expect, long update);

 public native boolean compareAndSwapInt(Object obj, long offset,int expect, int update);

通过Unsafe源码分析可知,只提供了3中CAS操作,分别是compareAndSwapObject、compareAndSwapLong、compareAndSwapInt

我们在看一下AtomicBoolean类的实现:

    public final boolean compareAndSet(boolean expect, boolean update) {
        int e = expect ? 1 : 0;//转换成int类型
        int u = update ? 1 : 0;//转换成int类型
        return unsafe.compareAndSwapInt(this, valueOffset, e, u);
    }

通过以上代码可以发现,AtomicBoolean先把boolean类型的参数转换成int类型,然后再调用Unsafe的compareAndSwapInt来进行CAS操作。因此,对于char、float、double类型的变量也可以用类似的思路实现。

原子更新数组

java.util.concurrent.atomic包提供了3个原子更新数组的类:

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

    AtomicInter常用方法如下:

  • addAndGet(int i,int delta):以原子方式将输入值与数组中索引i的元素相加。

  • boolean compareAndSet(int i,int expect,int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值
public class Test {
     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]);

 }

}

输出结果为:
3
1

原子更新引用类型

如果要原子更新多个变量,需要使用原子更新引用类型提供的类。java.util.concurrent.atomic包提供了3个类:

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

AtomicReference示例如下:

public class AtomicReferenceTest {
    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;
        }

    }
    public static AtomicReference atomicReference=new AtomicReference();
    public static void main(String[] args){
        AtomicReferenceTest.User user = new User("Tom", 15);
        atomicReference.set(user);
        User updateUser = new User("Jack",16);
        atomicReference.compareAndSet(user, updateUser);
        System.out.println(atomicReference.get().getName());
        System.out.println(atomicReference.get().getOld());
    }
}

输出结果:
jack
16

原子更新字段类

如果需要原子更新某个类的字段时,需要使用原子更新字段类,java.util.concurrent.atomic提供了3个类:

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

原子更新类的字段,需要两步:

  • 第一步:因为原子更新字段类都是抽象类,每次使用的时候必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。
  • 第二步:更新类的字段必须使用 public volatile修饰

AtomicIntegerFieldUpdater示例如下:


public class AtomicIntegerFieldUpdaterTest {
    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;
        }

    }
    private static AtomicIntegerFieldUpdater aifu =  AtomicIntegerFieldUpdater.newUpdater(User.class,"old");
    public static void main(String[] args){
        User user = new User("Tom", 15);
        System.out.println(aifu.getAndIncrement(user));//old加1,但是仍然会输出15
        System.out.println(aifu.get(user));//16
    }


}

输入结果如下;
15
16

你可能感兴趣的:(Java)