Java在高并发场景下实现点赞计数器

 更新时间:2023年06月28日 16:01:57   作者:SSPo  
点赞计数器的本质就是对某个变量在高并发情况下的修改,这篇文章主要为大家介绍了Java实现点赞计数器的示例代码,感兴趣的小伙伴可以了解一下

点赞计数器的本质就是对某个变量在高并发情况下的修改,volatile关键字只能保证变量的可见性和有序性,不能保证其原子性

使用方案有如下选择:

1. Synchronized 关键字

package concurrent;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
class ClickResource{
    int number = 0;
    public synchronized void clickBySynchronized(){number++;}
}
// 100个线程, 每个线程点赞 100万 次, 求准确率和效率
public class AccumulatorCompareDemo {
    public static final int _1w = 10000;
    public static final int threadNumber = 100;
    public static void main(String[] args) throws InterruptedException {
        ClickResource clickResource = new ClickResource();
        long startTime  = System.currentTimeMillis();
        CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
        for (int i = 0; i < threadNumber; i++) {
            new Thread(()->{
                try {
                    for (int j = 0; j < 100*_1w; j++) {
                        clickResource.clickBySynchronized();
                    }
                }finally {
                    countDownLatch1.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch1.await();
        long endTime  = System.currentTimeMillis();
        System.out.println("所用时间="+(endTime-startTime)+"ms"+"    最终点赞次数="+clickResource.number);
    }
}
// synchronized       悲观锁
// 所用时间=2258ms    最终点赞次数=100000000

2. 使用 AtomicLong 原子类

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
class ClickResource{
    int number = 0;
    AtomicLong atomicLong = new AtomicLong(0);
    public void clickByAtomicLong(){atomicLong.getAndIncrement();}
}
// 100个线程, 每个线程点赞 100万 次, 求准确率和效率
public class AccumulatorCompareDemo {
    public static final int _1w = 10000;
    public static final int threadNumber = 100;
    public static void main(String[] args) throws InterruptedException {
        ClickResource clickResource = new ClickResource();
        long startTime  = System.currentTimeMillis();
        CountDownLatch countDownLatch = new CountDownLatch(threadNumber);
        for (int i = 0; i < threadNumber; i++) {
            new Thread(()->{
                try {
                    for (int j = 0; j < 100*_1w; j++) {
                        clickResource.clickByAtomicLong();
                    }
                }finally {
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch.await();
        long endTime  = System.currentTimeMillis();
        System.out.println("所用时间="+(endTime-startTime)+"ms"+"    最终点赞次数="+clickResource.atomicLong);
    }
}
// CAS - 轻量级锁 - 调用 Unsafe 类的 cas 方法 - 底层操作系统原子指令 
// 所用时间=1177ms    最终点赞次数=100000000

3.使用 LongAdder 类

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
class ClickResource{
    int number = 0;
    LongAdder longAdder = new LongAdder();
    public void clickByLongAdder(){longAdder.increment();}
}
// 100个线程, 每个线程点赞 100万 次, 求准确率和效率
public class AccumulatorCompareDemo {
    public static final int _1w = 10000;
    public static final int threadNumber = 100;
    public static void main(String[] args) throws InterruptedException {
        ClickResource clickResource = new ClickResource();
        long startTime  = System.currentTimeMillis();
        CountDownLatch countDownLatch = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
        CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);
        for (int i = 0; i < threadNumber; i++) {
            new Thread(()->{
                try {
                    for (int j = 0; j < 100*_1w; j++) {
                        clickResource.clickByLongAdder();
                    }
                }finally {
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch.await();
        long endTime  = System.currentTimeMillis();
        System.out.println("所用时间="+(endTime-startTime)+"ms"+"    最终点赞次数="+clickResource.longAdder);
    }
}
//所用时间=141ms    最终点赞次数=100000000

4. 使用 LongAccumulator 类

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
class ClickResource{
    int number = 0;
    LongAccumulator longAccumulator = new LongAccumulator((x,y)->x+y,0);
    public void clickByLongAccumulator(){longAccumulator.accumulate(1);}
}
// 100个线程, 每个线程点赞 100万 次, 求准确率和效率
public class AccumulatorCompareDemo {
    public static final int _1w = 10000;
    public static final int threadNumber = 100;
    public static void main(String[] args) throws InterruptedException {
        ClickResource clickResource = new ClickResource();
        long startTime  = System.currentTimeMillis();
        CountDownLatch countDownLatch = new CountDownLatch(threadNumber);
        for (int i = 0; i < threadNumber; i++) {
            new Thread(()->{
                try {
                    for (int j = 0; j < 100*_1w; j++) {
                        clickResource.clickByLongAccumulator();
                    }
                }finally {
                    countDownLatch.countDown();
                }
            },String.valueOf(i)).start();
        }
        countDownLatch.await();
        long endTime  = System.currentTimeMillis();
        System.out.println("所用时间="+(endTime-startTime)+"ms"+"    最终点赞次数="+clickResource.longAccumulator);
    }
}
// 所用时间=150ms    最终点赞次数=100000000

LongAccumulator 和 LongAdder 底层调用的方法相同,主要区别是,LongAdder只能初始为0,且只能做加法操作,LongAccumulator 能初始成任何数

需要注意的是:

LongAdder 和 LongAccumulator 并不保证最终获取的数据是完全准确无误的,也许会有少许偏差,但在高并发的点赞场景下牺牲少量的数据精确性来保证高性能是完全能接受的

以上就是Java在高并发场景下实现点赞计数器的详细内容,更多关于Java点赞计数器的资料请关注脚本之家其它相关文章!

相关文章

  • 浅谈Java文件执行顺序、main程序入口的理解

    浅谈Java文件执行顺序、main程序入口的理解

    这篇文章主要介绍了Java文件执行顺序、main程序入口的理解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Spring中@ExceptionHandler注解的使用方式

    Spring中@ExceptionHandler注解的使用方式

    这篇文章主要介绍了Spring中@ExceptionHandler注解的使用方式,@ExceptionHandler注解我们一般是用来自定义异常的,可以认为它是一个异常拦截器(处理器),需要的朋友可以参考下
    2024-01-01
  • Spring MVC参数传递中文乱码解决方法分享

    Spring MVC参数传递中文乱码解决方法分享

    这篇文章主要介绍了Spring MVC参数传递中文乱码解决方法分享,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12
  • java操作PDF文件方法之转换、合成、切分

    java操作PDF文件方法之转换、合成、切分

    最近需要做⼀个把多个pdf报告合并成⼀个以⽅便预览的需求,下面这篇文章主要给大家介绍了关于java操作PDF文件方法之转换、合成、切分的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • Java如何配置IDEA自定义注释

    Java如何配置IDEA自定义注释

    在IDEA中设置自动创建类和方法的注释可以提高编码效率,确保代码的一致性和可读性,首先,对于创建类的注释,可以通过修改File→Settings→File and Code Templates→Class的模板来实现,其次,对于方法注释
    2024-10-10
  • Spring boot route Controller接收参数常用方法解析

    Spring boot route Controller接收参数常用方法解析

    这篇文章主要介绍了Spring boot route Controller接收参数常用方法解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • SpringBoot实现数据导入导出与报表生成的完整教程

    SpringBoot实现数据导入导出与报表生成的完整教程

    本文将带你掌握Spring Boot数据导入导出与报表生成的核心概念与使用方法,包括数据导入导出的定义与特点,帮你学会在实际开发中处理数据导入导出与报表生成问题
    2026-04-04
  • Xml中使用foreach遍历对象实现代码

    Xml中使用foreach遍历对象实现代码

    这篇文章主要介绍了Xml中使用foreach遍历对象实现代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-12-12
  • SpringBoot接口数据如何实现优雅的脱敏问题

    SpringBoot接口数据如何实现优雅的脱敏问题

    这篇文章主要介绍了SpringBoot接口数据如何实现优雅的脱敏问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • IDEA安装阿里代码规范插件的步骤图文详解

    IDEA安装阿里代码规范插件的步骤图文详解

    这篇文章主要介绍了IDEA安装阿里代码规范插件的步骤,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-12-12

最新评论