【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());
    }

}

【Java并发】聊聊LongAdder应用场景及其原理_第1张图片

原理

【Java并发】聊聊LongAdder应用场景及其原理_第2张图片
【Java并发】聊聊LongAdder应用场景及其原理_第3张图片
【Java并发】聊聊LongAdder应用场景及其原理_第4张图片
在这里插入图片描述
【Java并发】聊聊LongAdder应用场景及其原理_第5张图片

在这里插入图片描述

【Java并发】聊聊LongAdder应用场景及其原理_第6张图片

源码解读

【Java并发】聊聊LongAdder应用场景及其原理_第7张图片

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

【Java并发】聊聊LongAdder应用场景及其原理_第8张图片

【Java并发】聊聊LongAdder应用场景及其原理_第9张图片

【Java并发】聊聊LongAdder应用场景及其原理_第10张图片

【Java并发】聊聊LongAdder应用场景及其原理_第11张图片

你可能感兴趣的:(#,并发编程,java,开发语言)