• Java多线程(6)----浅谈13个原子操作类


    1,前言

    Java从JDK5开始提供了java.util.concurrent.atomic包,包中的原子操作类提供一种用法简单、性能高效、线程安全地更新一个变量的方式。

    因为变量的类型有很多种,所以在Atomic包一共提供了13个类,属于4种类型的原子更新方式,分别是:

    • 原子更新基本类型
    • 原子更新数组
    • 原子更新引用
    • 原子更新属性(字段)

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

    值得一提的是,以AtomicInteger其内部用于存储目标整数值的变量是用volatile进行修饰的

    public class AtomicInteger extends Number implements java.io.Serializable { 
        
        ......
        
        private volatile int value;
    		
        public AtomicInteger(int initialValue) {
            value = initialValue;
        }
        
        ......
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2,原子操作类

    2.1,原子更新基本类型

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

    三个类提供的方法几乎一模一样,所以以AtomicInteger为例

    方法描述
    public final int addAndGet(int delta)以原子方式将输入的数值和实例中的值相加,并返回结果
    public final boolean compareAndSet(int expect, int update)如果输入的数值等于预期值,则以原子方式将该值设置为输入的值
    public final int getAndIncrement() 以原子方式将当前值加1,注意,这里返回的是自增前的值
    public final void lazySet(int newValue)最终会设置为newValue,使用lazySet设置值后,可能会导致其他线程在一小段时间内还是可以读到旧的值
    public final int getAndSet(int newValue)用原子的方式设置位newValue,并返回旧值

    Atomic包提供了3种基本类型的原子更新,以AtomicBoolean源码为例:

        public final boolean compareAndSet(boolean expect, boolean update) {
            int e = expect ? 1 : 0;
            int u = update ? 1 : 0;
            return unsafe.compareAndSwapInt(this, valueOffset, e, u);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以发现它是先把Boolean转为整型,在使用compareAndSwapInt进行CAS,所以原子更新char,float,double变量也可以用类似的思路来实现。

    2.2,原子更新数组

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

    • AtomicIntegerArray:原子更新整型数组里的元素
    • AtomicLongArray:原子更新长整型数组里的元素
    • AtomicReferenceArray:原子更新引用类型数组里的元素
    public static void main(String[] args) {
        static int[] value = new int[]{12};
        static AtomicIntegerArray ai = new AtomicIntegerArray(value);
        //getAndSet(下标,新元素)方法是用原子方式将新元素放置于指定下标位置,并返回旧值
        ai.getAndSet(03);
        System.out.println(ai.get(0)); //输出 3
        System.out.println(value[0]); //输出 1
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

        public AtomicIntegerArray(int[] array) {
            // Visibility guaranteed by final field guarantees
            this.array = array.clone();
        }
    
    • 1
    • 2
    • 3
    • 4

    2.3,原子更新引用类型

    原子更新基本类型的AtomicInteger只能更新一个变量,如果是要原子更新多个变量,就需要使用这个原子更新引用类型提供的类

    • AtomicReference:原子更新引用类型
    • AtomicReferenceFieldUpdater:原子更新引用类型里的字段
    • AtomicMarkableReference:原子更新带有标志位的引用类型

    AtomicReference为例

    public class AtomicReferenceTest {
        public static AtomicReference<user> atomicUserRef = new
            AtomicReference<user>();
        public static void main(String[] args) {
            User user = new User("conan"15);
            atomicUserRef.set(user);
            User updateUser = new User("Shinichi"17); 
            //用CAS的方式更新引用
            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;
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2.4,原子更新字段类

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

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

    AtomicIntegerFieldUpdater为例:

    public class AtomicIntegerFieldUpdaterTest {
        // 创建原子更新器,并设置需要更新的对象类和对象的属性
        //由于AtomicIntegerFieldUpdater是一个抽象类,所以使用的时候需要使用静态方法:newUpadter()创建一个更新器,并且设置想要更新的类和属性;并且更新类的字段(属性)必须使用 public volatile 修饰符
        private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.
            newUpdater(User.class"old");
        public static void main(String[] args) { // 设置柯南的年龄是 10 岁
            User conan = new User("conan"10);
            // 柯南长了一岁,但是仍然会输出旧的年龄 ===》输出:10
            System.out.println(a.getAndIncrement(conan));
            // 输出柯南现在的年龄 ===》输出:11
            System.out.println(a.get(conan));
        }
        public static class User {
            private String name;
            public volatile int old;
            public User(String name, int old) {
                this.name = name;
                this.old = old;
            }
            public String getName() {
                return name;
            }
            public int getOld() {
                return old;
            }
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
  • 相关阅读:
    中大型商业银行堡垒机升级改造方案!必看!
    CMSIS-DSP lib 矩阵运算示例和源码
    Threejs实现一个园区
    Linux如何远程连接服务器?
    Go简单入门
    TypeScript简记(一)
    2024年华为HCIA-DATACOM新增题库(H12-811)
    nvm 配置国内镜像
    LabVIEW中编程更改进程的优先级
    7个关键词总结2022上半年智慧城市发展
  • 原文地址:https://blog.csdn.net/weixin_41043607/article/details/125461570