高并发下的计数器实现方式:AtomicLong、LongAdder、LongAccumulator

本文主要是介绍高并发下的计数器实现方式:AtomicLong、LongAdder、LongAccumulator,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

图片

一、前言

计数器是并发编程中非常常见的一个需求,例如统计网站的访问量、计算某个操作的执行次数等等。在高并发场景下,如何实现一个线程安全的计数器是一个比较有挑战性的问题。本文将介绍几种常用的计数器实现方式,包括AtomicLong、LongAdder和LongAccumulator,并深入讲解其中的CAS操作。

二、计数器

计数器是一种非常基础的数据结构,用于记录某个事件发生的次数。在并发编程中,由于多个线程可能同时对计数器进行修改,因此需要保证计数器的线程安全性。

三、AtomicLong

AtomicLong是Java中的一个原子类,主要作用是对长整形进行原子操作,保证并发情况下数据的安全性。它实现了一系列线程安全的方法,包括初始化为特定值和以原子方式设置当前值等。

AtomicLong的核心机制是通过CAS(Compare and Swap)操作来确保并发安全性。CAS是一种无锁算法,其核心思想是:如果内存中的值V符合预期值A,则将内存中值修改为B,否则不进行任何操作。整个过程是原子的,不会出现线程安全问题。在高并发环境下,当大量线程同时竞争更新同一个原子变量时,只有一个线程的CAS会成功,其他线程会不断尝试直到成功,这就可能造成大量线程竞争失败后,通过无限循环不断尝试自旋尝试CAS操作,白白浪费了CPU资源。

图片

图里可以看出在高并发情况下,当有大量线程同时去更新一个变量,任意一个时间点只有一个线程能够成功,绝大部分的线程在尝试更新失败后,会通过自旋的方式再次进行尝试,这样严重占用了 CPU 的时间片,进而导致系统性能问题。

多线程并发下AtomicLong实现计数器demo:


import java.util.concurrent.atomic.AtomicLong;public class AtomicLongCounter {
private AtomicLong counter = new AtomicLong(0);public void increment() {
long oldValue, newValue;
do {oldValue = counter.get();newValue = oldValue + 1;} while (!counter.compareAndSet(oldValue, newValue));}public long getCount() {
return counter.get();}public static void main(String[] args) throws InterruptedException {AtomicLongCounter counter = new AtomicLongCounter();
int threadCount = 10;Thread[] threads = new Thread[threadCount];for (int i = 0; i < threadCount; i++) {threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {counter.increment();}});threads[i].start();}for (int i = 0; i < threadCount; i++) {threads[i].join();}System.out.println("计数器的值:" + counter.getCount());}
}

四、LongAdder

LongAdder是Java 8新增的一个类,主要用于解决高并发下的计数问题。与AtomicLong不同,LongAdder内部采用了分段锁技术,将一个大的计数空间分成若干个小的空间进行累加操作。每个小空间都有一个独立的锁,当多个线程同时对不同的小空间进行累加操作时,它们可以并行执行,从而提高了并发性能。

图片

如图所示,LongAdder 设计思想上,采用分段的方式降低并发冲突的概率。通过维护一个基准值 base 和 Cell 数组。

多线程并发下LongAdder实现计数器demo:


import java.util.concurrent.atomic.LongAdder;public class LongAdderCounter {
private final LongAdder longAdder = new LongAdder();public void increment() {longAdder.increment();}public long getCount() {
return longAdder.sum();}public static void main(String[] args) throws InterruptedException {LongAdderCounter counter = new LongAdderCounter();
int threadCount = 10;Thread[] threads = new Thread[threadCount];for (int i = 0; i < threadCount; i++) {threads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {counter.increment();}});threads[i].start();}for (int i = 0; i < threadCount; i++) {threads[i].join();}System.out.println("计数器的值:" + counter.getCount());}
}

五、LongAccumulator

LongAccumulator是Java 8新增的一个类,用于实现自定义的累加操作。它提供了一种简单而灵活的方式来实现复杂的累加逻辑。LongAccumulator内部维护了一个累加结果和一个标识位,当调用accumulate方法时,会根据标识位的值来决定是否直接返回结果还是进入累加逻辑。这种方式可以有效地避免重复计算和线程竞争问题。


import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.util.LongAccumulator;public class LongAccumulatorCounter {
public static void main(String[] args) {
SparkConf conf = new SparkConf().setAppName("LongAccumulatorCounter").setMaster("local");
JavaSparkContext sc = new JavaSparkContext(conf);LongAccumulator longAccumulator = sc.longAccumulator();JavaRDD<Integer> rdd = sc.parallelize(Arrays.asList(1, 2, 3, 4, 5), 2);rdd.foreachPartition(partition -> {
for (int value : partition) {longAccumulator.add(value);}});System.out.println("累加器的值:" + longAccumulator.value());sc.stop();}
}

六、CAS(Compare and Swap)

CAS 全称:compare and swap,比较并交换。CAS操作是上述三种计数器实现方式的核心机制之一。它通过比较内存中的值和预期值是否相等来判断是否需要进行更新操作。如果相等,则将内存中的值修改为新值;否则不做任何操作。整个过程是原子的,不会出现线程安全问题。但是需要注意的是,在高并发场景下,当多个线程同时竞争同一个原子变量时,可能会出现“ABA”问题。即当一个线程读取了内存中的值A之后,另一个线程将其修改为B再修改为A,此时第一个线程再次读取该变量时会发现它的值仍然是A而不是B。为了解决这个问题,可以使用版本号等方式来解决“ABA”问题,使用Java提供的AtomicStampedReference 类。

七、总结

阿里巴巴推荐使用 LongAdder, 原因主要有以下几点:

高并发性能:LongAdder 采用分段锁的策略,可以避免 AtomicLong 中的竞争问题,提高并发性能。在分布式系统中,高并发性能是非常重要的。

可扩展性:LongAdder 支持可扩展性,可以通过增加更多的段来提高性能。这对于需要处理大量请求的分布式系统来说是非常有利的。

代码简单易懂:虽然LongAdder 的代码相对复杂一些,但是相对于 AtomicLong 来说更容易理解和维护。这对于开发人员来说是非常重要的。

更好的适用场景:阿里巴巴推荐使用 LongAdder 主要是因为在分布式系统中需要一个高性能、高可用的计数器实现。而 LongAdder 正好符合这个需求。

图片

这篇关于高并发下的计数器实现方式:AtomicLong、LongAdder、LongAccumulator的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/575420

相关文章

Python中bisect_left 函数实现高效插入与有序列表管理

《Python中bisect_left函数实现高效插入与有序列表管理》Python的bisect_left函数通过二分查找高效定位有序列表插入位置,与bisect_right的区别在于处理重复元素时... 目录一、bisect_left 基本介绍1.1 函数定义1.2 核心功能二、bisect_left 与

VSCode设置python SDK路径的实现步骤

《VSCode设置pythonSDK路径的实现步骤》本文主要介绍了VSCode设置pythonSDK路径的实现步骤,包括命令面板切换、settings.json配置、环境变量及虚拟环境处理,具有一定... 目录一、通过命令面板快速切换(推荐方法)二、通过 settings.json 配置(项目级/全局)三、

pandas实现数据concat拼接的示例代码

《pandas实现数据concat拼接的示例代码》pandas.concat用于合并DataFrame或Series,本文主要介绍了pandas实现数据concat拼接的示例代码,具有一定的参考价值,... 目录语法示例:使用pandas.concat合并数据默认的concat:参数axis=0,join=

Python程序打包exe,单文件和多文件方式

《Python程序打包exe,单文件和多文件方式》:本文主要介绍Python程序打包exe,单文件和多文件方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录python 脚本打成exe文件安装Pyinstaller准备一个ico图标打包方式一(适用于文件较少的程

java中BigDecimal里面的subtract函数介绍及实现方法

《java中BigDecimal里面的subtract函数介绍及实现方法》在Java中实现减法操作需要根据数据类型选择不同方法,主要分为数值型减法和字符串减法两种场景,本文给大家介绍java中BigD... 目录Java中BigDecimal里面的subtract函数的意思?一、数值型减法(高精度计算)1.

Python验证码识别方式(使用pytesseract库)

《Python验证码识别方式(使用pytesseract库)》:本文主要介绍Python验证码识别方式(使用pytesseract库),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全... 目录1、安装Tesseract-OCR2、在python中使用3、本地图片识别4、结合playwrigh

C#代码实现解析WTGPS和BD数据

《C#代码实现解析WTGPS和BD数据》在现代的导航与定位应用中,准确解析GPS和北斗(BD)等卫星定位数据至关重要,本文将使用C#语言实现解析WTGPS和BD数据,需要的可以了解下... 目录一、代码结构概览1. 核心解析方法2. 位置信息解析3. 经纬度转换方法4. 日期和时间戳解析5. 辅助方法二、L

Spring中管理bean对象的方式(专业级说明)

《Spring中管理bean对象的方式(专业级说明)》在Spring框架中,Bean的管理是核心功能,主要通过IoC(控制反转)容器实现,下面给大家介绍Spring中管理bean对象的方式,感兴趣的朋... 目录1.Bean的声明与注册1.1 基于XML配置1.2 基于注解(主流方式)1.3 基于Java

使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)

《使用Python和Matplotlib实现可视化字体轮廓(从路径数据到矢量图形)》字体设计和矢量图形处理是编程中一个有趣且实用的领域,通过Python的matplotlib库,我们可以轻松将字体轮廓... 目录背景知识字体轮廓的表示实现步骤1. 安装依赖库2. 准备数据3. 解析路径指令4. 绘制图形关键

windows系统上如何进行maven安装和配置方式

《windows系统上如何进行maven安装和配置方式》:本文主要介绍windows系统上如何进行maven安装和配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录1. Maven 简介2. maven的下载与安装2.1 下载 Maven2.2 Maven安装2.