原子操作类

Posted ylcc-zyq

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原子操作类相关的知识,希望对你有一定的参考价值。

前言

在并发编程中,如果要想保证共享数据的安全性,我们一般都需要加锁。但是加锁会使得程序效率明显降低。

在java.util.concurrent.atomic包下,JDK中就为我们提供了使用CAS算法的无锁的原子操作类,可以提高并发编程程序的效率。

这些原子操作类的CAS算法都是利用Unsafe提供的方法实现的

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

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

public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);

原子操作类

操作基本类型

有以下三种类型

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

这三种类型的方法基本一致,以AtomicInteger为例

public final int get() ;//获取当前值
public final void set(int newValue) ;//设置新值
public final int getAndSet(int newValue) ;//先获取旧值,在获取新值
public final int getAndIncrement();//获取值,然后再自增
public final int getAndDecrement() ;//获取值,再自减
public final int incrementAndGet();//先自增,再获取值
public final int decrementAndGet();//先自减,再获取值
public final int getAndAdd(int delta);//先获取值,再加上新值
public final int addAndGet(int delta);//先加上新值,再获取值
public final boolean compareAndSet(int expect, int update);//如果输入的值等于预期值,则以原子方式将该值设置为输入的值
public final void lazySet(int newValue);//最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

操作数组

对数组的原子操作,提供了以下的类

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

这三个类的操作与上边也基本一致,只不过在更改值的时候要带上下标。

常用的方法为

public final int get(int i) ;//获取索引为i的元素值
public final boolean compareAndSet(int i, int expect, int update);//如果当前值等于预期值,则以原子方式将数组位置i的元素设置为update值。

操作引用类型

有如下类:

  • AtomicReference: 原子更新引用类型。
  • AtomicReferenceFieldUpdater: 原子更新引用类型的字段。
  • AtomicMarkableReferce: 原子更新带有标记位的引用类型。可以避免ABA问题(我们知道CAS是通过将旧值与预期值进行比较,相同在更新新值,ABA问题就是,旧值被其他线程从A变为B后又被改为A的情况)

以AtomicReference为例:

public class TestAtomicReference {
    public static void main(String[] args) {
        User user = new User("张三");
        User user2 = new User("李四");
        AtomicReference<User> reference = new AtomicReference<>(user);
        System.out.println(reference.compareAndSet(user, user2));
        System.out.println(reference.get().getName());
    }
}

class User{
    public String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

这三个类的操作方法基本一致,不过AtomicReferenceFieldUpdater略有不同,更新的字段必须用volatile修饰。

更新字段类

  • AtomicIntegerFieldUpdater: 原子更新整型的字段的更新器。
  • AtomicLongFieldUpdater: 原子更新长整型字段的更新器。
  • AtomicStampedFieldUpdater: 原子更新带有版本号的引用类型。(也能避免ABA问题)
  • AtomicReferenceFieldUpdater: 这个在上面的描述中也出现过。

通过泛型指定引用类型和字段类型以及字段名,然后就可以通过compareAndSet方法修改对应类型对象的相应字段。

我们就以AtomicReferenceFieldUpdater为例。

public class TestAtomicReferenceFieldUpdater {
    public static void main(String[] args) {
        Person person = new Person();
        AtomicReferenceFieldUpdater<Person, String> fieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Person.class, String.class, "name");
        System.out.println(fieldUpdater.compareAndSet(person, "张三", "李四"));
        System.out.println(fieldUpdater.get(person));
    }
}
class Person{
    //要用volatile修饰
    public volatile String name="张三";

}

以上是关于原子操作类的主要内容,如果未能解决你的问题,请参考以下文章

使用AtomicInteger原子类代替i++线程安全操作

原子片段:原子编辑器中的多行片段

原子操作AtomicReference

java中的原子操作类AtomicInteger及其实现原理

java笔记java中的AtomicInteger原子操作类

原子操作类原子操作类详细介绍