比AtomicLong更高效的并发计数器

2024-01-26 06:08

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

我喜欢新鲜玩意儿,而Java 8里面就有[url=http://www.javacodegeeks.com/2014/03/5-features-in-java-8-that-will-change-how-you-code.html]不少[/url]。这回我准备介绍一下我的一个最爱——并发计数器。这是一组新的类,用于维护多个线程并发读写的计数器。新的API带来了显著的性能提升,同时还保证了接口的简单易用。

多核时代来临了之后,大家都开始使用并发计数器,我们先来看一下Java迄今为止提供了哪些实现方式,它们的性能和这个新的API相比,又有什么不同。

脏计数器——选择这种方式意味着多线程直接并发读写一个普通对象或者静态字段。不幸的是,这么做是行不通的。有两个原因,一个是在Java里, A+= B操作不是原子的。如果你打开编译后的字节码看一下,你会发现至少有四条指令——第一条是从堆里将字段值加载到线程栈里,第二条是加载要增加的值,第三条指令将它们进行相加,第四条则将结果写回到字段中。

如果多个线程同时在同一个内存位置进行这个操作,你的一个写操作很有可能就丢掉了,因为另一个线程可能会覆盖了它的值。还有一个很恶心的事就是这个值的可见性。下面还会详细介绍到。

新手非常容易犯这样的错误,而这样的问题却很难发现。如果你发现团队中有人这么做,最好能帮我个小忙。在你的数据库里面搜一下我的名字“Tal Weiss"。如果你发现我在里面——赶紧把我的记录删掉。这样我会感觉舒服点。

synchronzied——这是最基础的同步操作了,只要你在读写值,它就会阻塞住其它的所有线程。这种方式的确行得通,不过肯定的是,你的程序运行起来会像[url=https://www.youtube.com/watch?v=1w_XVPImsqQ]DMV排的长队[/url]那样。

读写锁(RWLock)——这个和基础的Java锁相比就巧妙了些,它可以让你区分出那些要修改值因此需要阻塞别人的进程以及那些只是读取值不需要进入临界区的。虽然这个方法有的时候很高效(比如写线程的数量比较少的话),但还是相当无语,因为当你获取写锁的时候还是会阻塞住其它线程的执行。

volatile——这个经常会被误用的关键字会让JIT编译停止在运行时进行机器码的优化工作,因此字段一旦有更新别的线程马上就能看到。

它会使得JIT编译器经常玩的一些把戏比如说调整赋值语句的顺序这些无法进行。JIT编译器有可能会改变字段的赋值顺序。什么,你再说一遍?是的,你听的没错。这个神秘的小把戏使得它可以减小程序访问全局堆的次数,同时它还能保证不会影响到你的程序的执行。这真是有点偷偷摸摸的感觉。

那什么时候应该使用volatile计数器?如果你只有一个线程在更新一个值,而多个线程在读的话,这是个很合适的场景。因为完全没有竞争。

你可能会问为什么都使用它就完了?因为如果有多个线程在更新的话就会有问题了。由于A+=B不是一个原子操作,这么做的话可能会覆盖掉别人写的话。在Java 8以前,这种情况你就只能用AtomicInteger了。

AtomicInteger——这组类使用了处理器的CAS (compare-and-swap)指令来更新计数器的值。听起来不错吧?一半一半吧。由于它直接使用机器指令来设置值,因此对其它线程的影响最小。不好的一面是如果它和别的线程有竞争赋值失败了,它会继续重试。在高并发的条件 下,这就成了一个自旋锁,线程会在一个无限的循环内不断的尝试赋值,直到成功为止。我们可不太想看到这种局面。Java 8来了,还带来了LongAdders。

Java 8 Adders——这是个非常棒的新的API,我对它的仰慕有如滔滔江水连绵不绝。从使用者的角度来说,它很像AtomicInteger。只需要创建一个LongAdder对象,然后使用intValue()以及add()方法来获取和设置它的值。而奇迹就发生在这一切的背后。

如果由于竞争这个类的CAS操作失败了的话,它会要添加的值存到一个线程本地的内部的cell对象里。当intValue()方法调用 的时候,它把这些cell的值加到总和里。这样就减少了CAS重试或者阻塞别的线程的情况。真不错的想法。

说的也差不多了。我们来看看它的真本事。我们做了如下的一个基准测试:把一个计数器设置为0,然后多个线程开始读取并进行自增。当计数器到达10^8的时候停止。我们在一个4核的i7处理器上运行这个测试。

我用了10个线程来运行这个基准测试——读写分别使用5个线程来进行,这样的话会出现严重的竞争条件:

注意:脏读和volatile都有可能产生脏值。

[img]http://a3ab771892fd198a96736e50.javacodegeeks.netdna-cdn.com/wp-content/uploads/2014/04/table_april-16_02-2.jpg[/img]

测试的代码在[url=https://github.com/takipi/counters-benchmark]这里[/url]。

[b]结论[/b]

[*]并发的Adder类和AtomicInteger相比有60~100%的性能提升。
[*]增加线程不会对结果有太大影响,除非是使用锁的情况。
[*]注意到如果使用synchronized或者读写锁,性能会有很大的损耗——慢了一个数量级!

如果你已经在代码里使用到它了——我会感到非常高兴。

译注:想深入了解LongAdders的工作原理的话,可以读下并发编程网上的[url=http://ifeve.com/atomiclong-and-longadder/]这篇文章[/url]。

原创文章转载请注明出处:[url=http://it.deepinmind.com]http://it.deepinmind.com[/url]

[url=http://www.takipiblog.com/2014/04/16/java-8-longadders-the-fastest-way-to-add-numbers-concurrently/]英文原文链接[/url]

这篇关于比AtomicLong更高效的并发计数器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/645862

相关文章

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

Python基于微信OCR引擎实现高效图片文字识别

《Python基于微信OCR引擎实现高效图片文字识别》这篇文章主要为大家详细介绍了一款基于微信OCR引擎的图片文字识别桌面应用开发全过程,可以实现从图片拖拽识别到文字提取,感兴趣的小伙伴可以跟随小编一... 目录一、项目概述1.1 开发背景1.2 技术选型1.3 核心优势二、功能详解2.1 核心功能模块2.

基于Python构建一个高效词汇表

《基于Python构建一个高效词汇表》在自然语言处理(NLP)领域,构建高效的词汇表是文本预处理的关键步骤,本文将解析一个使用Python实现的n-gram词频统计工具,感兴趣的可以了解下... 目录一、项目背景与目标1.1 技术需求1.2 核心技术栈二、核心代码解析2.1 数据处理函数2.2 数据处理流程

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

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

Python使用FFmpeg实现高效音频格式转换工具

《Python使用FFmpeg实现高效音频格式转换工具》在数字音频处理领域,音频格式转换是一项基础但至关重要的功能,本文主要为大家介绍了Python如何使用FFmpeg实现强大功能的图形化音频转换工具... 目录概述功能详解软件效果展示主界面布局转换过程截图完成提示开发步骤详解1. 环境准备2. 项目功能结

python多线程并发测试过程

《python多线程并发测试过程》:本文主要介绍python多线程并发测试过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、并发与并行?二、同步与异步的概念?三、线程与进程的区别?需求1:多线程执行不同任务需求2:多线程执行相同任务总结一、并发与并行?1、

Python Pandas高效处理Excel数据完整指南

《PythonPandas高效处理Excel数据完整指南》在数据驱动的时代,Excel仍是大量企业存储核心数据的工具,Python的Pandas库凭借其向量化计算、内存优化和丰富的数据处理接口,成为... 目录一、环境搭建与数据读取1.1 基础环境配置1.2 数据高效载入技巧二、数据清洗核心战术2.1 缺失

Linux高并发场景下的网络参数调优实战指南

《Linux高并发场景下的网络参数调优实战指南》在高并发网络服务场景中,Linux内核的默认网络参数往往无法满足需求,导致性能瓶颈、连接超时甚至服务崩溃,本文基于真实案例分析,从参数解读、问题诊断到优... 目录一、问题背景:当并发连接遇上性能瓶颈1.1 案例环境1.2 初始参数分析二、深度诊断:连接状态与

使用Python和SQLAlchemy实现高效的邮件发送系统

《使用Python和SQLAlchemy实现高效的邮件发送系统》在现代Web应用中,邮件通知是不可或缺的功能之一,无论是订单确认、文件处理结果通知,还是系统告警,邮件都是最常用的通信方式之一,本文将详... 目录引言1. 需求分析2. 数据库设计2.1 User 表(存储用户信息)2.2 CustomerO

Java中的StringBuilder之如何高效构建字符串

《Java中的StringBuilder之如何高效构建字符串》本文将深入浅出地介绍StringBuilder的使用方法、性能优势以及相关字符串处理技术,结合代码示例帮助读者更好地理解和应用,希望对大家... 目录关键点什么是 StringBuilder?为什么需要 StringBuilder?如何使用 St