深入探讨Java中的LongAdder:使用技巧与避坑指南
文章目录
- 一、什么是LongAdder?
- 二、LongAdder的简单使用
- 示例代码:
- 三、LongAdder的工作原理
- 四、LongAdder的常见使用场景
- 五、使用LongAdder时的注意事项(避坑指南)
- 1. 不要滥用LongAdder
- 2. sum()方法与精度问题
- 3. 避免过度使用reset()
- 4. 不能用于CAS依赖的场景
- 六、LongAdder与AtomicLong的区别
- 七、总结
- 推荐阅读文章
在高并发编程中,如何高效地计数或累加值是一个常见问题。Java提供了很多工具来应对这些场景,其中
LongAdder 是一个在高并发环境下性能优于
AtomicLong的类。它是如何工作的?我们如何正确使用它,并避免常见的坑?接下来,我们将通过简单易懂的方式,帮助你理解
LongAdder。
一、什么是LongAdder?
LongAdder位于java.util.concurrent.atomic包中,是一种用于高效计数的类。它的功能类似于AtomicLong,但设计上更适合在高并发环境下使用。
AtomicLong依赖于底层的**CAS(Compare-And-Swap)**机制,它通过不断重试来保证原子性。然而,在极高并发的场景中,CAS操作可能会频繁失败,导致性能下降。而LongAdder通过将计数分散到多个单独的变量中,并在最后累加,减少了竞争,从而在高并发场景下提升性能。
二、LongAdder的简单使用
LongAdder的使用非常简单,与AtomicLong类似。你可以使用increment()方法进行累加,用sum()来获取总值。
示例代码:
import java.util.concurrent.atomic.LongAdder;public class LongAdderExample {public static void main(String[] args) {// 创建LongAdder实例LongAdder longAdder = new LongAdder();// 执行累加操作longAdder.increment();longAdder.increment();longAdder.add(10); // 增加指定值// 获取当前累加的总值long sum = longAdder.sum();System.out.println("总计数值: " + sum); // 输出:12// 重置LongAdderlongAdder.reset();System.out.println("重置后总值: " + longAdder.sum()); // 输出:0}
}
三、LongAdder的工作原理
LongAdder 的核心思想是分段累加,即通过将计数分散到多个变量中(称为“槽”或“单元”),每个线程在并发访问时操作不同的单元,减少竞争。最后,当你调用sum()方法时,所有的单元的值会被汇总,得到最终的总值。
这种设计在低竞争时开销较大,因为每次操作需要涉及多个变量,而在高并发时则能大幅减少CAS失败的重试,提高整体性能。
简单理解:
- 在低并发时,
AtomicLong表现会更好,因为不需要额外的分段。 - 在高并发场景下,
LongAdder避免了频繁的CAS冲突,能显著提升效率。
四、LongAdder的常见使用场景
LongAdder特别适合用于高并发计数器场景,例如:
- Web请求统计:记录每秒钟的访问请求数。
- 日志系统中的日志条目计数:用于记录日志条目在多线程写入的总数。
- 性能分析工具:高并发系统中某些操作的频次统计。
五、使用LongAdder时的注意事项(避坑指南)
虽然LongAdder的性能在高并发下非常出色,但使用时也有一些注意事项需要小心。
1. 不要滥用LongAdder
LongAdder的优势主要体现在高并发场景下。如果你的应用并发量较低或只是进行简单的累加操作,那么使用AtomicLong更为合适,因为LongAdder在低并发下反而会有更高的开销。
解决方法:评估你的应用场景,如果并发量不高,优先使用AtomicLong,只有在并发量较大时才使用LongAdder。
2. sum()方法与精度问题
由于LongAdder的累加操作是分散到多个单元的,sum()方法是对这些单元进行汇总。因此,当你调用sum()时,可能无法得到瞬时的精确值,特别是在多个线程正在同时进行累加操作时。
解决方法:如果你需要在一个时刻获得精确的计数值,LongAdder可能不适合你。对于大多数场景,这种近似值是可以接受的。
3. 避免过度使用reset()
LongAdder提供了reset()方法来重置计数器,但要小心:reset()只是将累加器清零,且不会对每个线程的单元做特殊处理。在高并发的情况下,reset()后的新累加操作可能会受到原先单元状态的影响,导致不一致的行为。
解决方法:尽量避免在并发操作过程中频繁使用reset(),如果必须使用,确保在恰当的时机(如所有操作已完成时)调用。
4. 不能用于CAS依赖的场景
虽然LongAdder在累加操作中表现出色,但它并不支持AtomicLong的CAS操作。如果你的应用场景需要进行基于比较的原子性操作(如compareAndSet()),那AtomicLong是你更好的选择。
六、LongAdder与AtomicLong的区别
| 功能/特性 | LongAdder | AtomicLong |
|---|---|---|
| 性能 | 高并发场景下性能优于AtomicLong | 在低并发场景下表现更好 |
| 原子性 | 适合累加操作,但不支持compareAndSet() | 支持compareAndSet()等CAS操作 |
| 开销 | 分散到多个单元,低并发时有额外开销 | 简单直接,开销较小 |
| 使用场景 | 高并发计数器 | 需要单步原子性操作或低并发计数场景 |
七、总结
LongAdder是什么? 它是AtomicLong的替代品,设计用于高并发环境下的高效计数。- 如何使用? 提供了
increment()、add()、sum()等简单的方法,帮助你进行线程安全的累加操作。 - 避坑指南? 注意避免在低并发下使用
LongAdder,小心reset()操作带来的潜在问题,并且LongAdder无法用于需要CAS操作的场景。
通过正确使用LongAdder,你可以在高并发场景下更高效地进行计数操作,但在选择它之前,务必先评估你的需求和场景,确保它是最佳选择。
推荐阅读文章
- 使用 Spring 框架构建 MVC 应用程序:初学者教程
- 有缺陷的 Java 代码:Java 开发人员最常犯的 10 大错误
- 如何理解应用 Java 多线程与并发编程?
- Java Spring 中常用的 @PostConstruct 注解使用总结
- 线程 vs 虚拟线程:深入理解及区别
- 深度解读 JDK 8、JDK 11、JDK 17 和 JDK 21 的区别
- 10大程序员提升代码优雅度的必杀技,瞬间让你成为团队宠儿!
- “打破重复代码的魔咒:使用 Function 接口在 Java 8 中实现优雅重构!”
- Java 中消除 If-else 技巧总结
- 线程池的核心参数配置(仅供参考)
- 【人工智能】聊聊Transformer,深度学习的一股清流(13)
- Java 枚举的几个常用技巧,你可以试着用用
- 如何理解线程安全这个概念?
- 理解 Java 桥接方法
- Spring 整合嵌入式 Tomcat 容器
- Tomcat 如何加载 SpringMVC 组件
相关文章:
深入探讨Java中的LongAdder:使用技巧与避坑指南
文章目录 一、什么是LongAdder?二、LongAdder的简单使用示例代码: 三、LongAdder的工作原理四、LongAdder的常见使用场景五、使用LongAdder时的注意事项(避坑指南)1. 不要滥用LongAdder2. sum()方法与精度问题3. 避免过度使用rese…...
【本科毕业设计】基于单片机的智能家居防火防盗报警系统
基于单片机的智能家居防火防盗报警系统 相关资料链接下载摘要Abstract第1章 绪论1.1课题的背景1.2 研究的目的和意义 第2章 系统总体方案设计2.1 设计要求2.2 方案选择和论证2.2.1 单片机的选择2.2.2 显示方案的选择 第3章 系统硬件设计3.1 整体方案设计3.1.1 系统概述3.1.2 系…...
C语言 动态数据结构的C语言实现单向链表-2
建立一个单向链表 在单向链表中查找节点---查找尾节点 在单向链表中查找节点 --- 查找第 n 个节点 向单向链表中插入一个节点 向单向链表的尾部插入一个节点 向单向链表中某节点后插入一个节点 向单向链表中插入一个节点 删除单向链表中的某一节点 链表 vs 数组 动态数据结构...
Github 2024-10-23C开源项目日报 Top10
根据Github Trendings的统计,今日(2024-10-23统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目10PLpgSQL项目1Redis - 内存数据库和数据结构服务器 创建周期:5411 天开发语言:C协议类型:BSD 3-Clause “New” or “Revised” Licen…...
ubuntu20.04 opencv4.0 /usr/local/lib/libgflags.a(gflags.cc.o): relocation报错解决
在一个只有ubuntu20.04的docker环境中配置opencv4.0.0, 什么库都没有,都要重新安装, 其他的问题在网上都找到了解决方案,唯独这个问题比较棘手: [ 86%] Linking CXX executable …/…/bin/opencv_annotation /usr/bin/ld: /usr/lo…...
android openGL ES详解——混合
一、混合概念 混合是一种常用的技巧,通常可以用来实现半透明。但其实它也是十分灵活的,你可以通过不同的设置得到不同的混合结果,产生一些有趣或者奇怪的图象。混合是什么呢?混合就是把两种颜色混在一起。具体一点,就…...
计网--物理层
目录 物理层的任务 1、常见概念 2、信道极限容量 3、传输介质 (1)导引型传输介质 (2)非导引型传输介质 4、信道复用技术 (1)频分 / 时分 复用 (2)波分复用WDM (…...
算法的学习笔记—数组中的逆序对(牛客JZ51)
😀前言 在算法和数据结构领域,"逆序对"是一个经典问题。它在数组中两个数字之间定义,若前面的数字大于后面的数字,则这两个数字组成一个逆序对。我们要做的就是,给定一个数组,找出数组中所有的逆…...
Golang | Leetcode Golang题解之第498题对角线遍历
题目: 题解: func findDiagonalOrder(mat [][]int) []int {m, n : len(mat), len(mat[0])ans : make([]int, 0, m*n)for i : 0; i < mn-1; i {if i%2 1 {x : max(i-n1, 0)y : min(i, n-1)for x < m && y > 0 {ans append(ans, mat[x…...
什么是全局污染?怎么避免全局污染?
全局污染(Global Pollution)是指在编程过程中,过度使用全局变量或对象导致命名冲突、代码可维护性下降及潜在错误增加的问题。在 JavaScript 等动态语言中,尤其需要关注全局污染的风险。 全局污染的影响 1. 命名冲突 3. 意外修改…...
C# 串口通信教程
串口通信(Serial Communication)是一种用于设备之间数据传输的常见方法,通常用于与外部硬件设备(如传感器、机器人、微控制器)进行通信。在 C# 中,System.IO.Ports 命名空间提供了与串口设备交互的功能&…...
PHP编程基础
PHP(Hypertext Preprocessor,超文本预处理器)是一种广泛使用的开源服务器端脚本语言,主要用于网页开发,同时也可以进行命令行脚本编写。以下是PHP编程的基础知识: 1. PHP文件结构 PHP文件通常以 .php 为扩…...
TwinCAT3下位机配置EAP通讯传递与接收变量
添加EAP设备 DEVICE中右键选择添加新项,添加EAP(EtherCAT Automation Protocal)选择Network Variables类型,如下图。 设置网络适配器来激活EAP,在Adapter中选择search,选择网络适配器后确定,…...
近似推断 - 期望最大化(EM)篇
前言 近似推断是统计学和机器学习中一个至关重要的领域,尤其在处理复杂模型和不完全数据时显得尤为重要。期望最大化( Expectation Maximization \text{Expectation Maximization} Expectation Maximization,简称 EM \text{EM} EM࿰…...
arp欺骗及其实验
ARP欺骗(ARP Spoofing)是一种网络攻击技术,攻击者通过伪造ARP(地址解析协议)消息,将其MAC地址与目标IP地址关联,从而实现对网络流量的截获、篡改或重定向。以下是ARP欺骗的详细信息:…...
HDU The Boss on Mars(容斥原理)
题目大意: ACM 有 n 名员工,现在是他们从老板那里拿薪水的时候了。所有员工都从 1 到 n 编号。原因不明,如果员工的工作编号是 k,他今年可以获得 k^4 Mars 美元。所以为 ACM 工作的员工非常富有。 因为员工人数太多,…...
nnUnet 大模型学习笔记(续):训练网络(3d_fullres)以及数据集标签的处理
目录 1. 数据集处理 1.1 实现脚本 1.2 json文件 2. 设置读取路径 2.1 设置路径 2.2 数据集转换 2.3 数据集预处理 2.4 训练(3d_fullres) 3. 训练结果展示 关于nnUnet 数据集的处理和环境搭建,参考上文:第四章:nnUnet大模…...
Java中的数据结构与集合源码
目录 一、数据结构 1.1 数据结构概念 1.2 研究对象 1.3 常见存储结构 1.3.1 数组 1.3.2 链表 1.单向链表 2.双向链表 1.3.3 二叉树 1.3.4 栈(FILO,先进后出) 1.3.5 队列(FIFO,先进先出) 二、集合…...
Java应用程序的测试覆盖率之设计与实现(三)-- jacoco cli 客户端
一、背景 上文已把覆盖率数据采集好了,并提供远程连接的tcp地址及端口。 jacoco cli文档jacoco cli jar包 jacococli.jar 我下载好了,放在github工程里。 本文主要是介绍如何使用jacoco cli 客户端读取并生成覆盖率报告。 二、使用 1、dump覆盖率统…...
Deepin V23 / 统信UOS 下安装与配置 tftp
几个月前,我将开发系统从 ubuntu 切换到 Deepin,当时写过一篇文章《使用国产操作系统作为开发系统》。几个月下来,没有感觉有什么不适应,Ubuntu 能做的事情,在 Deepin 上都能做。而且有 UOS 应用商店的加持,…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
[10-3]软件I2C读写MPU6050 江协科技学习笔记(16个知识点)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
Go 并发编程基础:通道(Channel)的使用
在 Go 中,Channel 是 Goroutine 之间通信的核心机制。它提供了一个线程安全的通信方式,用于在多个 Goroutine 之间传递数据,从而实现高效的并发编程。 本章将介绍 Channel 的基本概念、用法、缓冲、关闭机制以及 select 的使用。 一、Channel…...
