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对象深度克隆的详解

    深入JAVA对象深度克隆的详解

    本篇文章是对JAVA对象深度克隆进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • 利用JSONObject.toJSONString()包含或排除指定的属性

    利用JSONObject.toJSONString()包含或排除指定的属性

    这篇文章主要介绍了利用JSONObject.toJSONString()包含或排除指定的属性,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-03-03
  • Java日常练习题,每天进步一点点(14)

    Java日常练习题,每天进步一点点(14)

    下面小编就为大家带来一篇Java基础的几道练习题(分享)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧,希望可以帮到你
    2021-07-07
  • Java数据结构之顺序表的实现

    Java数据结构之顺序表的实现

    线性表(linear list)是n个具有相同特性的数据元素的有限序列。顺序表是常见的线性表之一,本文将详细讲讲顺序表的原理与实现,需要的可以参考一下
    2022-08-08
  • Java并发编程学习之ThreadLocal源码详析

    Java并发编程学习之ThreadLocal源码详析

    这篇文章主要给大家介绍了关于Java并发编程学习之源码分析ThreadLocal的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-06-06
  • 一文搞懂设计模式中的单例模式

    一文搞懂设计模式中的单例模式

    这篇文章主要介绍了一文搞懂设计模式中的单例模式,单例模式是最简单的设计模式之一,属于创建型模式,它提供了一种创建对象的方式,确保只有单个对象被创建,需要的朋友可以参考下
    2023-08-08
  • 全面了解JAVA_BaseDAO数据处理类

    全面了解JAVA_BaseDAO数据处理类

    下面小编就为大家带来一篇全面了解JAVA_BaseDAO数据处理类。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-07-07
  • 运用springboot搭建并部署web项目的示例

    运用springboot搭建并部署web项目的示例

    这篇文章主要介绍了运用springboot搭建并部署web项目的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • 在Spring Boot中如何使用数据缓存

    在Spring Boot中如何使用数据缓存

    本篇文章主要介绍了在Spring Boot中如何使用数据缓存,具有一定的参考价值,有兴趣的可以了解一下。
    2017-04-04
  • springboot整合EHCache的实践方案

    springboot整合EHCache的实践方案

    EhCache是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。这篇文章给大家介绍了springboot整合EHCache的实践方案,需要的朋友参考下
    2018-01-01

最新评论