美文网首页
JAVA并发-Atomic包

JAVA并发-Atomic包

作者: handsomemao666 | 来源:发表于2021-01-03 09:45 被阅读0次

    Atomic包核心

    Atomic包里的类基本都是使用Unsafe实现的包装类,核心操作是CAS原子操作。
    compare and swap,比较和替换技术,将预期值与当前变量的值比较(compare),如果相等则使用新值替换(swap)当前变量,否则不作操作。
    现代CPU已广泛支持CAS指令,如果不支持,那么JVM将使用自旋锁,与互斥锁一样,两者都需先获取锁才能访问共享资源,但互斥锁会导致线程进入睡眠,而自旋锁会一直循环等待直到获取锁。
    另外,有一点需要注意的是CAS操作中的ABA问题,即将预期值与当前变量的值比较的时候,即使相等也不能保证变量没有被修改过,因为变量可能由A变成B再变回A,解决该问题,可以给变量增加一个版本号,每次修改变量时版本号自增,比较的时候,同时比较变量的值和版本号即可。

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

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

    包括:AtomicBoolean:原子更新布尔类型;AtomicInteger:原子更新整型;AtomicLong:原子更新长整型。
    下面以AtomicInteger举个例子:

    // 请求总数
    public static int clientTotal = 5000;
    
    // 同时并发执行的线程数
    public static int threadTotal = 200;
    
    public static AtomicInteger count = new AtomicInteger(0);
    
    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final Semaphore semaphore = new Semaphore(threadTotal);
        final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
        for (int i = 0; i < clientTotal ; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    add();
                    semaphore.release();
                } catch (Exception e) {
                    log.error("exception", e);
                }
                countDownLatch.countDown();
            });
        }
        countDownLatch.await();
        executorService.shutdown();
        log.info("count:{}", count.get());
    }
    
    private static void add() {
        count.incrementAndGet();
        // count.getAndIncrement();
    }
    

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

    原子方式更新数组

    以下三个类是以原子方式更新数组:
    AtomicIntegerArray:原子更新整型数组里的元素。
    AtomicLongArray:原子更新长整型数组里的元素。
    AtomicReferenceArray:原子更新引用类型数组里的元素。
    以AtomicIntegerArray为例,其方法与AtomicInteger很像,多了个数组下标索引:

    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为例:

    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为例:

    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;
        }
    }
    
    LongAdder

    jdk8中的LongAdder在并发竞争激烈的情况下,效率更高。

    相关文章

      网友评论

          本文标题:JAVA并发-Atomic包

          本文链接:https://www.haomeiwen.com/subject/ouhuoktx.html