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点赞计数器的资料请关注脚本之家其它相关文章!

相关文章

  • Spring Boot集成Swagger2项目实战

    Spring Boot集成Swagger2项目实战

    在日常的工作中,我们往往需要给前端(WEB端、IOS、Android)或者第三方提供接口,这个时候我们就需要给他们提供一份详细的API说明文档。这篇文章我们就来分享一种API文档维护的方式,即通过Swagger来自动生成Restuful API文档
    2018-01-01
  • Springboot使用put、delete请求报错405的处理

    Springboot使用put、delete请求报错405的处理

    这篇文章主要介绍了Springboot使用put、delete请求报错405的处理方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • SpringBoot集成Redisson实现延迟队列的场景分析

    SpringBoot集成Redisson实现延迟队列的场景分析

    这篇文章主要介绍了SpringBoot集成Redisson实现延迟队列,本文通过场景分析实例代码相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • Spring AOP访问目标方法的参数操作示例

    Spring AOP访问目标方法的参数操作示例

    这篇文章主要介绍了Spring AOP访问目标方法的参数操作,结合实例形式详细分析了spring面向切面AOP访问目标方法的参数相关实现步骤与操作注意事项,需要的朋友可以参考下
    2020-01-01
  • java图搜索算法之DFS与BFS详解

    java图搜索算法之DFS与BFS详解

    这篇文章主要为大家介绍了java数据结构中可以秒杀一切图算法的DFS与BFS作用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-11-11
  • mybatis主表与明细表一对多的同时插入操作方法

    mybatis主表与明细表一对多的同时插入操作方法

    对主表(采购申请表)和明细表(申请物资表)同时进行插入操作insert,怎么实现呢,下面给大家分享mybatis主表与明细表一对多的同时插入操作方法,感兴趣的朋友一起看看吧
    2023-02-02
  • Java中锁的分类与使用方法

    Java中锁的分类与使用方法

    这篇文章主要给大家介绍了关于Java中锁分类与使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • Java中字节流和字符流的理解(超精简!)

    Java中字节流和字符流的理解(超精简!)

    Java通过称为流的抽象来执行I/O操作,下面这篇文章主要给大家介绍了关于Java中字节流和字符流理解,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • springmvc如何使用POJO作为参数

    springmvc如何使用POJO作为参数

    这篇文章主要介绍了springmvc如何使用POJO作为参数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Spring--国内Java程序员用得最多的框架

    Spring--国内Java程序员用得最多的框架

    前几年面试最常问的且可以顺利拿到高薪的技能是Spring,随着Spring体系的壮大,除非你在简历上添加Spring Boot和Spring Cloud的技能,才可以打动面试官,而现在,除非是Spring全家桶的实战经验,否则难以让面试官高看
    2021-06-06

最新评论