• 原子操作类


    前言

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

    在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="张三";
    
    }
    
  • 相关阅读:
    IIS-Service Unavailable
    复制datatable,把类型变为字符串
    泛类型的使用
    线程间操作无效: 从不是创建控件“button1”的线程访问它。
    .dialog打开时执行方法
    更新系统时间
    复制对象
    如何安装windows服务
    ObjectARX创建文字
    设置cad进度条的arx代码
  • 原文地址:https://www.cnblogs.com/ylcc-zyq/p/12872900.html
Copyright © 2020-2023  润新知