原子操作类
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="张三";
}
以上是关于原子操作类的主要内容,如果未能解决你的问题,请参考以下文章
java中的原子操作类AtomicInteger及其实现原理