原子操作类解读

原子操作类概述

Java中提供了一些原子操作类,用于实现多线程环境下的数据同步问题。其中最常见的有以下几种:

1. AtomicBoolean:原子操作布尔型变量。

2. AtomicInteger:原子操作整型变量。

3. AtomicLong:原子操作长整型变量。

4. AtomicReference:原子操作引用类型变量。

5. AtomicStampedReference:原子操作带有版本号的引用类型变量。

这些原子操作类都提供了一些线程安全的方法,比如get、set、compareAndSet等,可以保证数据的原子性操作,避免了多线程环境下的数据竞争问题。

多线程环境不使用原子类保证线程安全i(基本数据类型)

public class AtomicIntegerTest {

    volatile int num=0;

    public int getNumber(){
        return num;
    }

    public synchronized void addNumber(){
         num++;
    }
}

多线程环境使用原子类保证线程安全i++(基本数据类型) 

public class AtomicIntegerTest {

    AtomicInteger atomicInteger = new AtomicInteger(0);

    public int getNum(){
        return atomicInteger.get();
    }

    public void addNum(){
        atomicInteger.getAndIncrement();
    }
}

volatile解决多线程内存不可见问题对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。也就是无法保证原子性。
如果是count++操作,使用如下类实现:
AtomicInteger count = new AtomicInteger();
count.addAndGet(1);
如果是JDK8,推荐使用LongAdder对象,比AtomicLong性能更好(减少乐观锁的重试次数)。

代码实战 (AtomicInteger)

AtomicInteger 常用API操作

public class AtomicIntegerTest {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger atomicInteger = new AtomicInteger(0);
        CountDownLatch latch = new CountDownLatch(50);
        for(int i=0;i<50;i++){
            new Thread(()->{
                try {
                    for(int j=0;j<10000;j++){
                        atomicInteger.getAndIncrement();
                    }
                }finally {
                    latch.countDown();
                }
            },""+i).start();
        }
      latch.await();
        System.out.println("最终结果"+atomicInteger.get());
    }
}

代码实战(数组类型原子类 )

AtomicIntegerArray是一个原子性的数组,它提供了一系列线程安全的方法。

1. AtomicIntegerArray(int length):创建一个给定长度的AtomicIntegerArray。

2. AtomicIntegerArray(int[] array):用一个给定的数组创建一个AtomicIntegerArray。

3. int length():返回AtomicIntegerArray的长度。

4. int get(int index):获取给定索引处的元素的值。

5. void set(int index, int newValue):设置给定索引处的元素为指定值。

6. void lazySet(int index, int newValue):设置给定索引处的元素为指定值,但不保证立即生效。

7. int getAndSet(int index, int newValue):设置给定索引处的元素为指定值,并返回先前的值。

8. boolean compareAndSet(int index, int expect, int update):如果当前值等于期望值,则将其设置为新值,并返回true。

9. boolean weakCompareAndSet(int index, int expect, int update):如果当前值等于期望值,则将其设置为新值,并返回true。在竞争高的情况下,此方法比compareAndSet更有效。

10. int getAndIncrement(int index):返回原始值并将给定索引处的元素增加1。

11. int getAndDecrement(int index):返回原始值并将给定索引处的元素减少1。

12. int getAndAdd(int index, int delta):返回原始值,并将给定索引处的元素增加给定的值。

13. void addAndGet(int index, int delta):将给定的值添加到给定索引处的元素,并返回新值。

14. boolean weakCompareAndSetAcquire(int index, int expect, int update):这是一个JDK13及以上版本的API,在Java11和12中不存在。这个方法和weakCompareAndSet方法一样,增加了一个内存屏障,即强制对该操作执行ACQUIRE半屏障,可用于加锁或者线程同步。

15. boolean weakCompareAndSetRelease(int index, int expect, int update):这是一个JDK13及以上版本的API,在Java11和12中不存在。这个方法和weakCompareAndSet方法一样,增加了一个内存屏障,即强制对该操作执行RELEASE半屏障,可用于解锁或者线程同步。

public class AtomicIntegerArrayTest {
    public static void main(String[] args) {
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
        for (int i=0;i<atomicIntegerArray.length();i++){
            System.out.println(atomicIntegerArray.get(i));
        }
        AtomicIntegerArray array = new AtomicIntegerArray(new int[]{1,2,3,4,5});
        array.getAndAdd(0, 999);
        System.out.println(array.get(0));
        array.getAndIncrement(4);
        System.out.println(array.get(4));
    }
}

代码实战(引用类型原子类)

 AtomicStampedReference携带版本号的引用类型原子类,可以解决ABA问题。解决修改过几次。状态戳原子引用AtomicMarkableReference原子更新带有标记位的引用类型对象。它的定义就是将状态戳简化为true/false。解决是否修改过,类似一次性筷子。状态戳(true/false)原子引用

public class AtomicMarkableReferenceTest {
    public static void main(String[] args) {
        AtomicMarkableReference<Integer> reference = new AtomicMarkableReference<>(1,false);
        new Thread(()->{
            boolean marked = reference.isMarked();
            Integer integer = reference.getReference();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            reference.compareAndSet(integer,99,marked,!marked);
        },"A").start();
        new Thread(()->{
            boolean marked = reference.isMarked();
            Integer integer = reference.getReference();
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("B线程修改结果:"+reference.compareAndSet(integer, 66, marked, !marked));
            System.out.println("最终标志位:"+reference.isMarked()+",最终结果:"+reference.getReference());
        },"B").start();
    }
}

代码实战(对象的属性修改原子类)

以一种线程安全的方式操作非线程安全对象内的某些字段

AtomiclntegerFieldUpdater:原子更新对象中int类型字段的值,基于反射的实用程序,可对指定类的指定volatile int字段进行原子更新。
AtomicLongFieldUpdater:原子更新对象中Long类型字段的值,基于反射的实用程序,可以对指定类的指定volatile long字段进行原子更新。
AtomicReferenceFieldUpdater:原子更新引用类型字段的值,基于反射的实用程序,可以对指定类的指定volatile引用字段进行原子更新。

使用要求:
更新的对象属性必须使用volatile修饰符。
因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。

 AtomiclntegerFieldUpdater代码

public class AtomicIntegerFieldUpdaterTest {

    public static void main(String[] args) {
        int size = 50;
        CountDownLatch latch = new CountDownLatch(size);
        Dog dog = new Dog();
        for (int i = 0; i < 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < 10000; j++) {
                        dog.add();
                    }
                } finally {
                    latch.countDown();
                }
            }, String.valueOf(i)).start();
        }
        try {
            latch.await(1, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        if (latch.getCount() > 0) {
            System.out.println("============执行超时============");
            return;
        }
        System.out.println(dog.age);
    }

    static class Dog {
        private volatile int age;
        private String name;

        AtomicIntegerFieldUpdater<Dog> updater = AtomicIntegerFieldUpdater.newUpdater(Dog.class, "age");

        public void add() {
            updater.getAndIncrement(this);
        }
    }
}

 AtomicReferenceFieldUpdater代码

public class AtomicReferenceFieldUpdaterTest {
    public static void main(String[] args) {
        Fish fish = new Fish();
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                fish.init();
            }).start();
        }
    }

    static class Fish {
        volatile Boolean isLive = Boolean.FALSE;
        AtomicReferenceFieldUpdater<Fish, Boolean> updater = AtomicReferenceFieldUpdater.newUpdater(Fish.class, Boolean.class, "isLive");

        public void init() {
            if (updater.compareAndSet(this, Boolean.FALSE, Boolean.TRUE)) {
                System.out.println("初始化的线程:" + Thread.currentThread().getName());
            } else {
                System.out.println("==========已被其他线程初始化=============");
            }
        }
    }
}

原子操作增强类(LongAdder LongAccumulator)

LongAdder只能用来计算加减法,且从零开始计算
LongAccumulator提供了自定义的函数操作

public class LongAdderTest {
    public static void main(String[] args) {
        LongAdder longAdder = new LongAdder();
        longAdder.add(3L);
        longAdder.add(5L);
        longAdder.increment();
        System.out.println(longAdder.sum());
        longAdder.decrement();
        System.out.println(longAdder.sum());

        LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
        longAccumulator.accumulate(6);
        longAccumulator.accumulate(2);
        System.out.println(longAccumulator.get());
    }
}

 性能比较代码

public class LogKpiCountTest {
    public static void main(String[] args) {
        LogKpiCount count = new LogKpiCount();
        int size = 100;
        CountDownLatch latch1 = new CountDownLatch(size);
        CountDownLatch latch2 = new CountDownLatch(size);
        CountDownLatch latch3 = new CountDownLatch(size);
        CountDownLatch latch4 = new CountDownLatch(size);
        Long startTime=null;
        startTime=System.currentTimeMillis();
        for (int i=0;i<size;i++){
            new Thread(()->{
                try {
                    for (int j = 0; j < 1000000; j++) {
                        count.synchronizedAdd();
                    }
                }finally {
                    latch1.countDown();
                }

            }).start();
        }
        try {
            latch1.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("synchronizedAdd执行耗时:"+(System.currentTimeMillis()-startTime)+",执行结果:"+count.num);

        startTime=System.currentTimeMillis();
        for (int i=0;i<size;i++){
            new Thread(()->{
                try {
                    for (int j = 0; j < 1000000; j++) {
                        count.atomicIntegerAdd();
                    }
                }finally {
                    latch2.countDown();
                }

            }).start();
        }
        try {
            latch2.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("atomicIntegerAdd执行耗时:"+(System.currentTimeMillis()-startTime)+",执行结果:"+count.atomicInteger.get());

        startTime=System.currentTimeMillis();
        for (int i=0;i<size;i++){
            new Thread(()->{
                try {
                    for (int j = 0; j < 1000000; j++) {
                        count.longAdderAdd();
                    }
                }finally {
                    latch3.countDown();
                }

            }).start();
        }
        try {
            latch3.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("longAdderAdd执行耗时:"+(System.currentTimeMillis()-startTime)+",执行结果:"+count.longAdder.sum());

        startTime=System.currentTimeMillis();
        for (int i=0;i<size;i++){
            new Thread(()->{
                try {
                    for (int j = 0; j < 1000000; j++) {
                        count.longAccumulatorAdd();
                    }
                }finally {
                    latch4.countDown();
                }

            }).start();
        }
        try {
            latch4.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("longAccumulatorAdd执行耗时:"+(System.currentTimeMillis()-startTime)+",执行结果:"+count.accumulator.get());
    }

    static class LogKpiCount{
        int num;
        AtomicInteger atomicInteger = new AtomicInteger();
        LongAdder longAdder = new LongAdder();
        LongAccumulator accumulator = new LongAccumulator((x,y)->x+y,0);
        public synchronized void synchronizedAdd(){
            num++;
        }

        public void atomicIntegerAdd(){
            atomicInteger.getAndIncrement();
        }

        public void longAdderAdd(){
            longAdder.increment();
        }

        public void longAccumulatorAdd(){
            accumulator.accumulate(1);
        }
    }
}

 synchronizedAdd执行耗时:3585,执行结果:100000000
atomicIntegerAdd执行耗时:1567,执行结果:100000000
longAdderAdd执行耗时:629,执行结果:100000000
longAccumulatorAdd执行耗时:132,执行结果:100000000

执行结果如下,发现synchronized与其他三个存在数量级上的差异,AtomicInteger与其他两个存在数量级上的差异。

LongAdder为什么这么快

LongAdder的基本思路就是分散热点,将value值分散到一个Cell数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。

sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前AtomicLong一个value的更新压力分散到多个value中去,从而降级更新热点。

猜你喜欢

转载自blog.csdn.net/m0_62436868/article/details/129894898
今日推荐