• Java中的Atomic包


    Atomic包的作用

    方便程序员在多线程环境下,无锁的进行原子操作

    Atomic包核心

    Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作;

    关于CAS

    compare and swap,比较和替换技术,将预期值与当前变量的值比较(compare),如果相等则使用新值替换(swap)当前变量,否则不作操作;

    现代CPU已广泛支持CAS指令,如果不支持,那么JVM将使用自旋锁,与互斥锁一样,两者都需先获取锁才能访问共享资源,但互斥锁会导致线程进入睡眠,而自旋锁会一直循环等待直到获取锁;

    另外,有一点需要注意的是CAS操作中的ABA问题,即将预期值与当前变量的值比较的时候,即使相等也不能保证变量没有被修改过,因为变量可能由A变成B再变回A,解决该问题,可以给变量增加一个版本号,每次修改变量时版本号自增,比较的时候,同时比较变量的值和版本号即可;

    Atomic包主要提供四种原子更新方式

    • 原子方式更新基本类型;
    • 原子方式更新数组;
    • 原子方式更新引用;
    • 原子方式更新字段;

    原子方式更新基本类型

    以下三个类是以原子方式更新基本类型

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

    以AtomicInteger为例,

    package concurrency;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class AtomicIntegerTest {
    
        static AtomicInteger ai = new AtomicInteger(1);
    
        public static void main(String[] args) {
            //相当于i++,返回的是旧值,看方法名就知道,先获取再自增
            System.out.println(ai.getAndIncrement());
            System.out.println(ai.get());
            //先自增,再获取
            System.out.println(ai.incrementAndGet());
            System.out.println(ai.get());
            //增加一个指定值,先add,再get
            System.out.println(ai.addAndGet(5));
            System.out.println(ai.get());
            //增加一个指定值,先get,再set
            System.out.println(ai.getAndSet(5));
            System.out.println(ai.get());
        }
    
    }

    注意:Atomic包提供了三种基本类型的原子更新,剩余的Java的基本类型还有char,float和double等,其更新方式可以参考AtomicBoolean的思路来现,AtomicBoolean是把boolean转成整型再调用compareAndSwapInt进行CAS来实现的,类似的short和byte也可以转成整形,float和double可以利用Float.floatToIntBits,Double.doubleToLongBits转成整形和长整形进行相应处理;

    原子方式更新数组

    以下三个类是以原子方式更新数组,

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

    以AtomicIntegerArray为例,其方法与AtomicInteger很像,多了个数组下标索引;

    package concurrency;
    
    import java.util.concurrent.atomic.AtomicIntegerArray;
    
    public class AtomicIntegerArrayTest {
    
        static int[] valueArr = new int[] { 1, 2 };
    
        //AtomicIntegerArray内部会拷贝一份数组
        static AtomicIntegerArray ai = new AtomicIntegerArray(valueArr);
    
        public static void main(String[] args) {
            ai.getAndSet(0, 3);
            //不会修改原始数组value
            System.out.println(ai.get(0));
            System.out.println(valueArr[0]);
        }
    
    }

    原子方式更新引用

    以下三个类是以原子方式更新引用,与其它不同的是,更新引用可以更新多个变量,而不是一个变量;

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

    以AtomicReference为例,

    package concurrency;
    
    import java.util.concurrent.atomic.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);
            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;
            }
        }
    }

    原子方式更新字段

    以下三个类是以原子方式更新字段,

    • AtomicIntegerFieldUpdater:原子更新整型字段的更新器。
    • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
    • AtomicStampedReference:原子更新带有版本号的引用类型,用于解决使用CAS进行原子更新时,可能出现的ABA问题。

    以AtomicIntegerFieldUpdater为例,

    package concurrency;
    
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    
    public class AtomicIntegerFieldUpdaterTest {
    
        private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater
                .newUpdater(User.class, "old");
    
        public static void main(String[] args) {
            User conan = new User("conan", 10);
            System.out.println(a.getAndIncrement(conan));
            System.out.println(a.get(conan));
        }
    
        public static class User {
            private String name;
            //注意需要用volatile修饰
            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;
            }
        }
    }

    参考资料

    http://ifeve.com/java-atomic/

    《JAVA并发编程实战》

  • 相关阅读:
    Lodop简短问答客户反馈篇 及排查步骤 及注册相关
    Win10图片打不开文件系统错误2147416359解决方法
    Lodop中特殊符号¥打印设计和预览不同
    LODOP安装参数 及静默安装
    LODOP打印安装到win的特殊字体
    Lodop打印设计(PRINT_DESIGN)介绍
    Lodop打印设计里的 打印项对齐
    System.out.println与System.err.println的区别(输出顺序!!!)
    享元模式
    Java中关于HashMap的元素遍历的顺序问题
  • 原文地址:https://www.cnblogs.com/chenpi/p/5375805.html
Copyright © 2020-2023  润新知