• 原子操作工具类


    原子操作工具类

    1、测试synchronize互斥锁和其他原子工具类修改属性的效率

    package com.bilibili.juc.atomics;
    
    
    import lombok.Data;
    
    import javax.lang.model.element.VariableElement;
    import java.util.HashMap;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.atomic.AtomicLong;
    import java.util.concurrent.atomic.LongAccumulator;
    import java.util.concurrent.atomic.LongAdder;
    
    /**
     * 测试synchronize和其他Long类型的原子操作工具类的效率
     */
    class ClickNumber //资源类
    {
    
        int number = 0;
    
        public synchronized void clickBySynchronized() {
            number++;
        }
        //AtomicLong是采用的CAS执行的原子操作,在低并发效率还可以,高并发不行
        AtomicLong atomicLong = new AtomicLong(0);
    
        public void clickByAtomicLong() {
            atomicLong.getAndIncrement();
        }
        //采用了一种分段的方式来处理原子操作。它将内部的数值分成多个段(Cells),每个段都有自己的原子计数。不同线程对不同的段进行增加操作,
        // 减少了竞争,从而提高了性能。
        LongAdder longAdder = new LongAdder();
    
        public void clickByLongAdder() {
            longAdder.increment();
        }
    
        //定义一个二元操作函数 ,x是当前值,y是accumulate()的值,初始值为0   0+1,1+1,2+1,3+1
        LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);
    
        public void clickByLongAccumulator() {
            longAccumulator.accumulate(1);
        }
    
    }
    
    /**
     * 需求: 50个线程,每个线程100W次,总点赞数出来
     */
    public class AccumulatorCompareDemo {
        public static final int _1W = 10000;
        public static final int threadNumber = 50;
    
        public static void main(String[] args) throws InterruptedException {
            ClickNumber clickNumber = new ClickNumber();
            long startTime;
            long endTime;
    
            CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
            CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
            CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
            CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);
    
            startTime = System.currentTimeMillis();
            //50个线程,每个线程累加100万次
            for (int i = 1; i <= threadNumber; i++) {
                new Thread(() -> {
                    try {
                        for (int j = 1; j <= 100 * _1W; j++) {
                            clickNumber.clickBySynchronized();
                        }
                    } finally {
                        //每完成一次计数器减一,为0时停止阻塞
                        countDownLatch1.countDown();
                    }
                }, String.valueOf(i)).start();
            }
            countDownLatch1.await();
            endTime = System.currentTimeMillis();
            System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t clickBySynchronized: " + clickNumber.number);
    
            startTime = System.currentTimeMillis();
            for (int i = 1; i <= threadNumber; i++) {
                new Thread(() -> {
                    try {
                        for (int j = 1; j <= 100 * _1W; j++) {
                            clickNumber.clickByAtomicLong();
                        }
                    } finally {
                        //每完成一次计数器减一,为0时停止阻塞
                        countDownLatch2.countDown();
                    }
                }, String.valueOf(i)).start();
            }
            countDownLatch2.await();
            endTime = System.currentTimeMillis();
            System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t clickByAtomicLong: " + clickNumber.atomicLong.get());
    
    
            startTime = System.currentTimeMillis();
            for (int i = 1; i <= threadNumber; i++) {
                new Thread(() -> {
                    try {
                        for (int j = 1; j <= 100 * _1W; j++) {
                            clickNumber.clickByLongAdder();
                        }
                    } finally {
                        //每完成一次计数器减一,为0时停止阻塞
                        countDownLatch3.countDown();
                    }
                }, String.valueOf(i)).start();
            }
            countDownLatch3.await();
            endTime = System.currentTimeMillis();
            System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t clickByLongAdder: " + clickNumber.longAdder.sum());
    
            startTime = System.currentTimeMillis();
            for (int i = 1; i <= threadNumber; i++) {
                new Thread(() -> {
                    try {
                        for (int j = 1; j <= 100 * _1W; j++) {
                            clickNumber.clickByLongAccumulator();
                        }
                    } finally {
                        countDownLatch4.countDown();
                    }
                }, String.valueOf(i)).start();
            }
            countDownLatch4.await();
            endTime = System.currentTimeMillis();
            System.out.println("----costTime: " + (endTime - startTime) + " 毫秒" + "\t clickByLongAccumulator: " + clickNumber.longAccumulator.get());
    
        }
    }
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134

    运行结果----costTime: 2559 毫秒 clickBySynchronized: 50000000
    ----costTime: 866 毫秒 clickByAtomicLong: 50000000
    ----costTime: 91 毫秒 clickByLongAdder: 50000000
    ----costTime: 55 毫秒 clickByLongAccumulator: 50000000

    Process finished with exit code 0

    2、整型数组原子操作工具类操作修改数组

    package com.bilibili.juc.atomics;
    
    import java.util.concurrent.atomic.AtomicIntegerArray;
    
    /**
     * 整型数组原子操作工具类操作修改数组
     */
    public class AtomicIntegerArrayDemo
    {
        public static void main(String[] args)
        {
            AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[5]);
            //AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(5);
            //AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1,2,3,4,5});
    
            for (int i = 0; i <atomicIntegerArray.length(); i++) {
                System.out.println(atomicIntegerArray.get(i));
            }
    
            System.out.println();
    
            int tmpInt = 0;
    
    
             //给指定的索引设置新值
            tmpInt = atomicIntegerArray.getAndSet(0,1122);
            System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));
    
            //给自定索引的数值加一
            tmpInt = atomicIntegerArray.getAndIncrement(0);
            System.out.println(tmpInt+"\t"+atomicIntegerArray.get(0));
        }
    }
    
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    运行结果:

    0
    0
    0
    0
    0

    0 1122
    1122 1123

    Process finished with exit code 0

    3、整型原子操作工具类

    package com.bilibili.juc.atomics;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    /**
     整型原子操作工具类
     */
    class MyNumber
    {
        AtomicInteger atomicInteger = new AtomicInteger();
    
        public void addPlusPlus()
        {
            atomicInteger.getAndIncrement();
        }
    }
    
    
    
    public class AtomicIntegerDemo
    {
        public static final int SIZE = 50;
    
        public static void main(String[] args) throws InterruptedException
        {
            MyNumber myNumber = new MyNumber();
            CountDownLatch countDownLatch = new CountDownLatch(SIZE);
            //50个线程,每个线程自增1000次,最后拿到打印结果50000
            for (int i = 1; i <=SIZE; i++) {
                new Thread(() -> {
                    try {
                        for (int j = 1; j <=1000; j++) {
                            myNumber.addPlusPlus();
                        }
                    } finally {
                        //每个线程执行完就会减一,计数器减到0就会停止阻塞
                        countDownLatch.countDown();
                    }
                },String.valueOf(i)).start();
            }
            //等待上面50个线程全部计算完成后,再去获得最终值
    
            //暂停几秒钟线程
            //try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
            //计数器减到0就会停止阻塞
            countDownLatch.await();
    
            System.out.println(Thread.currentThread().getName()+"\t"+"result: "+myNumber.atomicInteger.get());
        }
    }
    
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    运行结果:main result: 50000

    4、原子整型字段更新器

    用来操作类中整型字段

    package com.bilibili.juc.atomics;
    
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    
    /**
     * AtomicIntegerFieldUpdate 对类中的指定整型类型字段进行 原子自增操作
     */
    class BankAccount//资源类
    {
        String bankName = "CCB";
    
        //更新的对象属性必须使用 public volatile 修饰符。  JMM三大特性 原子性,可见性,有序性  ;volatile 不能保证原子性
        public volatile int money = 0;//钱数
    
        public void add()
        {
            money++;
        }
    
        //因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。
        AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =
                AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");
    
        //不加synchronized,保证高性能原子性,局部微创小手术
        public void transMoney(BankAccount bankAccount)
        {
            fieldUpdater.getAndIncrement(bankAccount);
        }
    
    
    }
    
    /**
     * 以一种线程安全的方式操作非线程安全对象的某些字段。
     *
     * 需求:
     * 10个线程,
     * 每个线程转账1000,
     * 不使用synchronized,尝试使用AtomicIntegerFieldUpdater来实现。
     */
    public class AtomicIntegerFieldUpdaterDemo
    {
        public static void main(String[] args) throws InterruptedException
        {
            BankAccount bankAccount = new BankAccount();
            CountDownLatch countDownLatch = new CountDownLatch(10);
    
            for (int i = 1; i <=10; i++) {
                new Thread(() -> {
                    try {
                        for (int j = 1; j <=1000; j++) {
                            //bankAccount.add();
                            bankAccount.transMoney(bankAccount);
                        }
                    } finally {
                        countDownLatch.countDown();
                    }
                },String.valueOf(i)).start();
            }
    
            countDownLatch.await();
    
            System.out.println(Thread.currentThread().getName()+"\t"+"result: "+bankAccount.money);
        }
    }
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    运行结果:main result: 10000

    5、原子引用字段更新器

    package com.bilibili.juc.atomics;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
    
    /**
     * 原子引用字段更新器
     */
    class MyVar //资源类
    {
        public volatile Boolean isInit = Boolean.FALSE;
    
        //把指定isInit的操作变为原子操作
        AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater =
                AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");
    
        public void init(MyVar myVar)
        {
            if (referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE))
            {
                System.out.println(Thread.currentThread().getName()+"\t"+"----- start init,need 2 seconds");
                //暂停几秒钟线程
                try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
                System.out.println(Thread.currentThread().getName()+"\t"+"----- over init");
            }else{
                System.out.println(Thread.currentThread().getName()+"\t"+"----- 已经有线程在进行初始化工作。。。。。");
            }
        }
    }
    
    
    /**
     * 需求:
     * 多线程并发调用一个类的初始化方法,如果未被初始化过,将执行初始化工作,
     * 要求只能被初始化一次,只有一个线程操作成功
     */
    public class AtomicReferenceFieldUpdaterDemo
    {
        public static void main(String[] args)
        {
            MyVar myVar = new MyVar();
    
            for (int i = 1; i <=5; i++) {
                new Thread(() -> {
                    myVar.init(myVar);
                },String.valueOf(i)).start();
            }
        }
    }
    
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    运行结果:

    1 ----- start init,need 2 seconds
    2 ----- 已经有线程在进行初始化工作。。。。。
    4 ----- 已经有线程在进行初始化工作。。。。。
    3 ----- 已经有线程在进行初始化工作。。。。。
    5 ----- 已经有线程在进行初始化工作。。。。。
    1 ----- over init

    6、标记引用工具类

    用它实现CAS(compare and swap)

    package com.bilibili.juc.atomics;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicMarkableReference;
    
    /**
    标记引用工具类用来实现CAS
     */
    public class AtomicMarkableReferenceDemo
    {
        static AtomicMarkableReference markableReference = new AtomicMarkableReference(100,false);
    
        public static void main(String[] args)
        {
            new Thread(() -> {
                boolean marked = markableReference.isMarked();
                System.out.println(Thread.currentThread().getName()+"\t"+"默认标识:"+marked);
                //暂停1秒钟线程(这就是为什么t2也是false),等待后面的T2线程和我拿到一样的模式flag标识,都是false
                try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
                //拿到期望的标识false ,把markableReference的标识设置为true
                markableReference.compareAndSet(100,1000,marked,!marked);
                boolean t1marked = markableReference.isMarked();
                System.out.println("t1修改后的mark:"+t1marked);
            },"t1").start();
    
            new Thread(() -> {
                boolean marked = markableReference.isMarked();
                System.out.println(Thread.currentThread().getName()+"\t"+"默认标识:"+marked);
    
                try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
                boolean beforeT2Update = markableReference.isMarked();
                System.out.println(Thread.currentThread().getName()+"\t"+"修改前的标识:"+marked);
                //如果 markableReference 中的引用是 100 且标志位的状态与 marked 变量相同,那么它将把引用更改为 2000,并将标志位的状态取反。
                boolean b = markableReference.compareAndSet(100, 2000, marked, !marked);
                System.out.println(Thread.currentThread().getName()+"\t"+"t2线程CAS修改result: "+b);
                System.out.println(Thread.currentThread().getName()+"\t"+markableReference.isMarked());
                System.out.println(Thread.currentThread().getName()+"\t"+markableReference.getReference());
            },"t2").start();
        }
    }
    
    /**
     *  CAS----Unsafe----do while+ABA---AtomicStampedReference,AtomicMarkableReference
     *
     *  AtomicStampedReference,version号,+1;
     *
     *  AtomicMarkableReference,一次,false,true
     *
     */
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    运行结果:

    t1 默认标识:false
    t2 默认标识:false
    t1修改后的mark:true
    t2 修改前的标识:false
    t2 t2线程CAS修改result: false
    t2 true
    t2 1000

    Process finished with exit code 0

    7、Long类型原子操作类 和自定义原子二元操作函数工具类

    package com.bilibili.juc.atomics;
    
    import java.util.concurrent.atomic.LongAccumulator;
    import java.util.concurrent.atomic.LongAdder;
    import java.util.function.LongBinaryOperator;
    
    /**
      Long类型原子操作类 采用了一种分段的方式来处理原子操作。它将内部的数值分成多个段(Cells),每个段都有自己的原子计数。不同线程对不同的段进行增加操作,
     减少了竞争,从而提高了性能。
     自定义原子二元操作函数工具类:LongAccumulator
     */
    public class LongAdderAPIDemo
    {
        public static void main(String[] args)
        {
            LongAdder longAdder = new LongAdder();
    
            longAdder.increment();
            longAdder.increment();
            longAdder.increment();
    
            System.out.println(longAdder.sum());
    
            //自定义原子二元操作函数工具类
            LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator()
            {
                @Override
                public long applyAsLong(long left, long right)
                {
                    return left + right;
                }
            },0);
    
            longAccumulator.accumulate(1);//1
            longAccumulator.accumulate(3);//4
    
            System.out.println(longAccumulator.get());
        }
    }
    
    
    • 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
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    运行结果:

    3

    4

    好了以上就是一些常用的原子操作工具类。

  • 相关阅读:
    【暑期每日一题】洛谷 P7798 [COCI2015-2016#6] PUTOVANJE
    [H5动画制作系列 ] Sprite Demo 的两种方法
    Java定时器
    面试-synchronized(java5以前唯一)和ReentrantLock的区别
    MySQL向自增列插入0失败问题
    【案例】| C++实现STMP发送邮件
    uniapp 中app通过视频链接获取封面
    Reactor和Proactor
    如何把路由器设备的LAN口地址为三大私网地址
    springboot中如何进行业务层测试事务回滚
  • 原文地址:https://blog.csdn.net/qq_45925197/article/details/132878681