• 【Java并发】聊聊LongAdder应用场景及其原理


    应用场景

    我们知道在实际的应用场景中,可能会对某个商品进行浏览次数进行迭代,或者抖音视频的点击,那么如何高效记录呢,首先如果是使用普通的num 进行多线程操作的话,那么一定会带来数据一致性问题,所以一般通过syn\lock,但是因为有加锁和解锁的操作,以及线程竞争过多的时候,导致线程上下切换。那么又没有一种高效的方式呢,就是使用无锁编程原子类,atomicInteger但是因为是使用CAS并且针对的是同一个数据进行cas操作,操作数据的粒度是一个,所以进一步的方式是使用longAdder,主要原理就是将数据的操作粒度分散,类似于hashmap的散列表方式。

    code

    
    /**
     * @author qxlx
     * @date 2023/10/15 9:52 AM
     */
    
    class MyNumber {
    
        public Long num = new Long(0);
    
        public synchronized long synAddNumer() {
            return num++;
        }
    
        public AtomicInteger atomicInteger = new AtomicInteger(0);
    
        public int atomicIntegerAddNumer() {
            return atomicInteger.getAndIncrement();
        }
    
        public LongAdder longAdder = new LongAdder();
        public int longAdderAddNumber(){
            longAdder.increment();
            return longAdder.intValue();
        }
    
    }
    
    public class LongAddrCalcDemo {
    
        public static final int THREAD_NUM = 50;
        public static final int ADD_NUM = 1000000;
    
        public static void main(String[] args) throws InterruptedException {
            MyNumber myNumber = new MyNumber();
            StopWatch stopWatch = new StopWatch();
    
            CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
            CountDownLatch countDownLatch2 = new CountDownLatch(THREAD_NUM);
            CountDownLatch countDownLatch3 = new CountDownLatch(THREAD_NUM);
    
            stopWatch.start("1.加锁方式耗时");
            for (int i = 0; i < THREAD_NUM; i++) {
                new Thread(()-> {
                    for (int j = 0; j < ADD_NUM ; j++) {
                        long numer = myNumber.synAddNumer();
                    }
                    countDownLatch.countDown();
                }).start();
            }
            countDownLatch.await();
            System.out.println("1.加锁方式,num:"+myNumber.num);
            stopWatch.stop();
    
    
            stopWatch.start("2.atomic");
            for (int i = 0; i < THREAD_NUM; i++) {
                new Thread(()-> {
                    for (int j = 0; j < ADD_NUM ; j++) {
                        long numer = myNumber.atomicIntegerAddNumer();
                    }
                    countDownLatch2.countDown();
                }).start();
            }
            countDownLatch2.await();
            System.out.println("2.atomic,num:"+myNumber.atomicInteger);
            stopWatch.stop();
    
    
            stopWatch.start("3.longadder");
            for (int i = 0; i < THREAD_NUM; i++) {
                new Thread(()-> {
                    for (int j = 0; j < ADD_NUM ; j++) {
                        long numer = myNumber.longAdderAddNumber();
                    }
                    countDownLatch3.countDown();
                }).start();
            }
            countDownLatch3.await();
            System.out.println("3.longadder,num:"+myNumber.longAdder);
            stopWatch.stop();
    
            System.out.println("date:"+stopWatch.prettyPrint());
        }
    
    }
    
    
    • 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

    在这里插入图片描述

    原理

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    源码解读

    在这里插入图片描述

       public void add(long x) {
            Cell[] as; long b, v; int m; Cell a;
            if ((as = cells) != null || !casBase(b = base, b + x)) {
                boolean uncontended = true;
                if (as == null || (m = as.length - 1) < 0 ||
                    (a = as[getProbe() & m]) == null ||
                    !(uncontended = a.cas(v = a.value, v + x)))
                    longAccumulate(x, null, uncontended);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

  • 相关阅读:
    第一章:最新版零基础学习 PYTHON 教程(第十三节 - Python 语句中的 –Python While 循环)
    计算机毕业设计之java+ssm网络作者与美工交流平台
    三十六、java版 SpringCloud分布式微服务云架构之Java 泛型
    【DPDK】Trace Library
    react-route的路由
    字符串常用方法 --- 字符串对象的属性 与 字符串对象的方法(上)
    【产品设计】素描最重要的目的是为了好看吗?
    短视频账号矩阵系统源码/技术源码分享/技术搭建架构
    网络协议 从入门到精通系列讲解 - 总目录
    使用 OpenGL 渲染会旋转 & 会变色的三角形(LearnOpenGL P3)
  • 原文地址:https://blog.csdn.net/jia970426/article/details/133825438