JUC包 — atomic包—AtomicLong,AtomicIntegerArray等类

之前看了AtomicInteger的使用,代码。我们再看下atomic包下的其他类。我们先对其进行简单的分类:

第一类:使用原子的方式更新基本类型

AtomicInterger
AtomicBoolean
AtomicLong
在之前的文章介绍过AtomicInterger:https://blog.csdn.net/java_yes/article/details/83864042
而AtomicBoolean、AtomicLong和AtomicInterger原理基本一样。就不过多介绍。

第二类:通过原子的方式更新数组里的某个元素

AtomicIntegerArray
举例:

    static int[] values = new int[10];
    static AtomicIntegerArray atomicIntegerArray =new AtomicIntegerArray(values);

使用普通int数组:

    @Test
    public void integerArrayTest() throws InterruptedException {
        for (int i = 0; i < 1000000; i++) {
            new Thread() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        for (int j = 0; j < 10; j++) {//所有元素+1
                            values[j]++;
                        }
                    }
                }
            }.start();
        }

        for (int i = 0; i < 10; i++) {
            System.out.print(values[i] + " ");
        }
    }

结果:
《JUC包 — atomic包—AtomicLong,AtomicIntegerArray等类》
使用AtomicIntegerArray:

    @Test
    public void atomicIntegerArrayTest() throws InterruptedException {
        for (int i = 0; i < 1000000; i++) {
            new Thread() {
                @Override
                public void run() {
                    for (int i = 0; i < 100; i++) {
                        for (int j = 0; j < 10; j++) {//所有元素+1
                            atomicIntegerArray.getAndIncrement(j);
                        }
                    }
                }
            }.start();
        }

        for (int i = 0; i < 10; i++) {
            System.out.print(atomicIntegerArray.get(i) + " ");
        }
    }

《JUC包 — atomic包—AtomicLong,AtomicIntegerArray等类》

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

AtomicLongArray
AtomicReferenceArray

第三类:原子更新引用

AtomicReference:原子更新引用类型
举例:

import org.testng.annotations.Test;
import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceTest {
    private static int threadCount = 10;
    public static AtomicReference<User> atomicUserRef = new AtomicReference<User>();

    @Test
    private void atomicReferenceTest() throws InterruptedException {
        new Thread() {
            @Override
            public void run() {
                for (int i = 1; i < 10; i++) {
                    User oldUser = atomicUserRef.get();
                    atomicUserRef.compareAndSet(oldUser, new User("name" + i, i));
                }
            }
        }.start();
        Thread.sleep(1000);
        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;
        }
    }
}

结果:
《JUC包 — atomic包—AtomicLong,AtomicIntegerArray等类》
AtomicStampedReference:原子更新引用类型里的字段
AtomicMarkableReference:原子更新带有标记位的引用类型

第四类:原子更新字段

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

第五类:.原子类型累加器

LongAccumulator
LongAdder
DoubleAccumulator
DoubleAdder

参考:https://blog.csdn.net/hanchao5272/article/details/79689366
先解释一下这个类是干嘛的
根据类前面的注释,大概翻译一下:

一个或多个变量,它们一起维护使用提供的函数更新的运行的long值。当跨线程争用更新accumulate时,变量集可以动态增长以减少争用。方法get(或者,等效longValue)在维护更新的变量之间返回当前值。

当多个线程更新一个公共值时,LongAccumulator通常优于AtomicLong,LongAccumulator用于收集统计信息,而不是用于细粒度同步控制。在低更新竞争下,这两个类具有相似的特性。但是,在高争用情况下,LongAccumulator预期吞吐量显著较高,而代价是较高的空间消耗。

线程内或线程间的累积顺序没有保证,也不能依赖,因此LongAccumulator仅适用于累积顺序无关的函数。提供的累加器函数应该是无副作用的,因为当尝试的更新由于线程之间的争用而失败时,可以重新应用该函数。LongAccumulator以当前值作为第一个参数,以给定的更新作为第二个参数。例如,为了维持运行的最大值,您可以提供{@code.::max}以及{@code…MIN_VALUE}作为标识。

这个类扩展Number类,但没有定义诸如equals、hashCode和compareTo之类的方法,因为期望实例发生变化,因此作为集合键没有用。

内部实现

原子类型累加器其实是应用了热点分离思想,这一点可以类比一下ConcurrentHashMap的设计思想。

热点分离简述:

将竞争的数据进行分解成多个单元,在每个单元中分别进行数据处理。
各单元处理完成之后,通过Hash算法进行计算求和,从而得到最终的结果。

热点分离优缺点:

热点分离的设计减小了锁的粒度,提高了高并发环境下的吞吐量
热点分离的设计需要划分额外的空间进行单元数据的存储,增大空间消耗

基本方法

下面以LongAdder为例,对原子类型累加器的基本方法进行学习:

  1. LongAdder():累加器只有一个无参的构造器,会构造一个sum=0的实例对象。
  2. increment():自增。increment():自增。
  3. decrement():自减。decrement():自减。
  4. add(delat):增量计算。add(delat):增量计算。
  5. sum():计算sum的和。sum():计算sum的和。
  6. reset():重置sum为0。reset():重置sum为0。
  7. sumThenReset():计算sum的和并且重置sum为0。sumThenReset():计算sum的和并且重置sum为0。
  8. intValue():获取sum的int形式(向下转型)。intValue():获取sum的int形式(向下转型)。
  9. floatValue():获取sum的float形式(向上转型)。floatValue():获取sum的float形式(向上转型)。
  10. doubleValue():获取sum的double形式(向上转型)。 doubleValue():获取sum的double形式(向上转型)。
    原文作者:JUC
    原文地址: https://blog.csdn.net/java_yes/article/details/84777133
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞