高性能并发计数器的比较

参考文档:https://baijiahao.baidu.com/s?id=1742540809477784106&wfr=spider&for=pc

一、常用的并发计数方法

1、synchronized

synchronized早期是一个重量级锁,因为线程竞争锁会引起操作系统用户态和内核态切换,浪费资源,效率不高。jdk1.6对 synchronized 做了性能优化,它会经历偏向锁,轻量级锁,最后才到重量级锁,性能提升显著。jdk1.7的 ConcurrentHashMap 是基于ReentrantLock的实现了锁,但在jdk1.8之后又替换成了 synchronized,籍此可见JVM团队对synchronized的信心;

2、原子更新整型 AtomicInteger、AtomicLong

JDK1.5提供了并发的Integer/Long的操作工具类 AtomicInteger、AtomicLong。AtomicLong底层是一个乐观锁,不用阻塞线程,其利用底层操作系统的CAS来保证原子性,即在一个死循环内不断执行CAS操作,直到成功。其缺陷是当并发较大时,大量线程不断CAS,存在多次的CAS操作失败,使CPU升高,效率降低。

AtomicLong value = new AtomicLong(0);
value.incrementAndGet();

3、LongAdder [单机]

在JDK8中新增了LongAdder,这是针对Long类型的数据的操作工具类。在ConcurrentHashMap中,对Map分割成多个segment,这样多个Segment的操作就可以并行执行,从而提高性能。在JDK8中,LongAdder与ConcurrentHashMap类似,将内部操作数据value分离成一个Cell数组,每个线程访问时,通过Hash等算法映射到其中一个Cell上。 计算最终的数据结果,则是各个Cell数组的累计求和。

4、Redisson分布式累加器 [分布式]

基于Redis的Redisson分布式长整型累加器(LongAdder)采用了与java.util.concurrent.atomic.LongAdder 类似的接口。通过利用客户端内置的 LongAdder 对象,为分布式环境下递增和递减操作提供了很高的性能。其性能最高比分布式 AtomicLong 对象快 10^4 倍。

RLongAddr itheimaLongAddr = redission.getLongAddr(“itheimaLongAddr”);
itheimaLongAddr.add(100);
itheimaLongAddr.increment();
itheimaLongAddr.increment();
itheimaLongAddr.sum();

基于Redis的Redisson分布式双精度浮点累加器(DoubleAdder)采用了与 java.util.concurrent.atomic.DoubleAdder 类似的接口。通过利用客户端内置的 DoubleAdder 对象,为分布式环境下递增和递减操作提供了很高的性能。其性能最高比分布式 AtomicDouble 对象快 10^4 倍。

RLongDouble itheimaDouble = redission.getLongDouble(“itheimaLongDouble”);
itheimaDouble.add(100);
itheimaDouble.increment();
itheimaDouble.increment();
itheimaDouble.sum();

二、并发计数方法性能测试

1、测试程序

    
    private long count = 0;

    @Test
    public void counterCompare() {
        this.counterCousum(1,   1000 * 1000);
        this.counterCousum(20,  1000 * 1000);
        this.counterCousum(40,  1000 * 1000);
        this.counterCousum(60,  1000 * 1000);
        this.counterCousum(100, 1000 * 1000);
    }

    /**
     * @param threadCount 线程数量
     * @param increTimes 每个线程自增次数
     */
    private void counterCousum(int threadCount, int increTimes) {

        long start;

        try {
            System.out.println(String.format("线程数量: %s, 自增次数: %s", threadCount, increTimes));

            start = System.currentTimeMillis();
            this.synchronizedTest(threadCount, increTimes);
            System.out.println("Synchronized 耗时: " + (System.currentTimeMillis() - start));

            start = System.currentTimeMillis();
            this.atomicLongTest(threadCount, increTimes);
            System.out.println("AtomicLong 耗时: " + (System.currentTimeMillis() - start));

            start = System.currentTimeMillis();
            this.longAdderTest(threadCount, increTimes);
            System.out.println("LongAdder 耗时: " + (System.currentTimeMillis() - start) + "\n");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void atomicLongTest(int threadCount, int times) throws InterruptedException {
        AtomicLong count = new AtomicLong();
        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < threadCount; i++) {
            threadList.add(new Thread(()-> {
                for (int j = 0; j < times; j++) {
                    count.incrementAndGet();
                }
            }));
        }

        for (Thread thread : threadList) {
            thread.start();
        }

        for (Thread thread : threadList) {
            thread.join();
        }
    }

    private void synchronizedTest(int threadCount, int times) throws InterruptedException {

        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < threadCount; i++) {
            threadList.add(new Thread(()-> {
                for (int j = 0; j < times; j++) {
                    add();
                }
            }));
        }

        for (Thread thread : threadList) {
            thread.start();
        }

        for (Thread thread : threadList) {
            thread.join();
        }
    }

    private synchronized void add() {
        count++;
    }

    private void longAdderTest(int threadCount, int times) throws InterruptedException {
        LongAdder count = new LongAdder();
        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < threadCount; i++) {
            threadList.add(new Thread(()-> {
                for (int j = 0; j < times; j++) {
                    count.increment();
                }
            }));
        }

        for (Thread thread : threadList) {
            thread.start();
        }

        for (Thread thread : threadList) {
            thread.join();
        }
    }

2、测试结果

在这里插入图片描述
synchronized、AtomicLong的耗时随着并发量的增大而增大,LongAdder的耗时保持相对稳定,性能优越 !!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值