Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
Java高并发教程
-> 原子操作增强类LongAdder、LongAccumulator
1、必须知道的几个概念
2、并发级别
3、有关并行的两个重要定律
4、JMM相关的一些概念
5、深入理解进程和线程
6、线程的基本操作
7、volatile与Java内存模型
8、线程组
9、用户线程和守护线程
10、线程安全和synchronized
11、中断线程的几种方式
12、JUC中ReentrantLock
13、JUC中的Condition
14、JUC中的LockSupport工具类
15、UC中的Semaphore(信号量)
16、JUC中的CountDownLatch
17、JUC中的循环栅栏CyclicBarrier
18、线程池
19、JUC中的Executor框架详解1
20、JUC中的Executor框架详解2
21、java中的CAS
22、java中的UnSafe类
23、JUC中的原子操作类
24、ThreadLocal、InheritableThreadLocal
25、JUC中的阻塞队列
26、JUC中一些常见的集合
27、实战:你的接口太慢了需要优化
28、实战:构建日志系统
29、实战:一起来搞懂限流
30、JUC中的CompletableFuture
31、等待线程完成的方式你知道几种?
32、原子操作增强类LongAdder、LongAccumulator
33、怎么演示公平锁和非公平锁
34、谷歌提供的一些好用的并发工具类
35、延迟队列 DelayQueue 详解
36、线程6种状态详解
37、如何实现一个通用的延迟队列?
上一篇:等待线程完成的方式你知道几种?
下一篇:怎么演示公平锁和非公平锁
<div style="display:none"></div> ## 本文主要内容 1. 4种方式实现计数器功能,对比其性能 2. 介绍LongAdder 3. 介绍LongAccumulator ## 来个需求 **一个jvm中实现一个计数器功能,需保证多线程情况下数据正确性。** 我们来模拟50个线程,每个线程对计数器递增100万次,最终结果应该是5000万。 我们使用4种方式实现,看一下其性能,然后引出为什么需要使用`LongAdder`、`LongAccumulator`。 ## 方式一:synchronized方式实现 ```java package com.itsoku.chat32; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.LongAccumulator; /** * 跟着阿里p7学并发,微信公众号:javacode2018 */ public class Demo1 { static int count = 0; public static synchronized void incr() { count++; } public static void main(String[] args) throws ExecutionException, InterruptedException { for (int i = 0; i < 10; i++) { count = 0; m1(); } } private static void m1() throws InterruptedException { long t1 = System.currentTimeMillis(); int threadCount = 50; CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { try { for (int j = 0; j < 1000000; j++) { incr(); } } finally { countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long t2 = System.currentTimeMillis(); System.out.println(String.format("结果:%s,耗时(ms):%s", count, (t2 - t1))); } } ``` 输出: ```java 结果:50000000,耗时(ms):1437 结果:50000000,耗时(ms):1913 结果:50000000,耗时(ms):386 结果:50000000,耗时(ms):383 结果:50000000,耗时(ms):381 结果:50000000,耗时(ms):382 结果:50000000,耗时(ms):379 结果:50000000,耗时(ms):379 结果:50000000,耗时(ms):392 结果:50000000,耗时(ms):384 ``` **平均耗时:390毫秒** ## 方式2:AtomicLong实现 ```java package com.itsoku.chat32; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicLong; /** * 跟着阿里p7学并发,微信公众号:javacode2018 */ public class Demo2 { static AtomicLong count = new AtomicLong(0); public static void incr() { count.incrementAndGet(); } public static void main(String[] args) throws ExecutionException, InterruptedException { for (int i = 0; i < 10; i++) { count.set(0); m1(); } } private static void m1() throws InterruptedException { long t1 = System.currentTimeMillis(); int threadCount = 50; CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { try { for (int j = 0; j < 1000000; j++) { incr(); } } finally { countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long t2 = System.currentTimeMillis(); System.out.println(String.format("结果:%s,耗时(ms):%s", count, (t2 - t1))); } } ``` 输出: ```java 结果:50000000,耗时(ms):971 结果:50000000,耗时(ms):915 结果:50000000,耗时(ms):920 结果:50000000,耗时(ms):923 结果:50000000,耗时(ms):910 结果:50000000,耗时(ms):916 结果:50000000,耗时(ms):923 结果:50000000,耗时(ms):916 结果:50000000,耗时(ms):912 结果:50000000,耗时(ms):908 ``` **平均耗时:920毫秒** `AtomicLong`内部采用CAS的方式实现,并发量大的情况下,CAS失败率比较高,导致性能比synchronized还低一些。并发量不是太大的情况下,CAS性能还是可以的。 `AtomicLong`属于JUC中的原子类,还不是很熟悉的可以看一下:[JUC中原子类,一篇就够了](/course/1/23) ## 方式3:LongAdder实现 先介绍一下`LongAdder`,说到LongAdder,不得不提的就是AtomicLong,AtomicLong是JDK1.5开始出现的,里面主要使用了一个long类型的value作为成员变量,然后使用循环的CAS操作去操作value的值,并发量比较大的情况下,CAS操作失败的概率较高,内部失败了会重试,导致耗时可能会增加。 **LongAdder是JDK1.8开始出现的,所提供的API基本上可以替换掉原先的AtomicLong**。LongAdder在并发量比较大的情况下,操作数据的时候,相当于把这个数字分成了很多份数字,然后交给多个人去管控,每个管控者负责保证部分数字在多线程情况下操作的正确性。当多线程访问的时,通过hash算法映射到具体管控者去操作数据,最后再汇总所有的管控者的数据,得到最终结果。相当于降低了并发情况下锁的粒度,所以效率比较高,看一下下面的图,方便理解: ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/32/3beb157f-17bf-4bd5-a9a5-1c0572a0799b.png) 代码: ```java package com.itsoku.chat32; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.LongAdder; /** * 跟着阿里p7学并发,微信公众号:javacode2018 */ public class Demo3 { static LongAdder count = new LongAdder(); public static void incr() { count.increment(); } public static void main(String[] args) throws ExecutionException, InterruptedException { for (int i = 0; i < 10; i++) { count.reset(); m1(); } } private static void m1() throws ExecutionException, InterruptedException { long t1 = System.currentTimeMillis(); int threadCount = 50; CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { try { for (int j = 0; j < 1000000; j++) { incr(); } } finally { countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long t2 = System.currentTimeMillis(); System.out.println(String.format("结果:%s,耗时(ms):%s", count.sum(), (t2 - t1))); } } ``` 输出: ```java 结果:50000000,耗时(ms):206 结果:50000000,耗时(ms):105 结果:50000000,耗时(ms):107 结果:50000000,耗时(ms):107 结果:50000000,耗时(ms):105 结果:50000000,耗时(ms):99 结果:50000000,耗时(ms):106 结果:50000000,耗时(ms):102 结果:50000000,耗时(ms):106 结果:50000000,耗时(ms):102 ``` **平均耗时:100毫秒** 代码中`new LongAdder()`创建一个LongAdder对象,内部数字初始值是0,调用`increment()`方法可以对LongAdder内部的值原子递增1。`reset()`方法可以重置`LongAdder`的值,使其归0。 ## 方式4:LongAccumulator实现 **LongAccumulator介绍** LongAccumulator是LongAdder的功能增强版。LongAdder的API只有对数值的加减,而LongAccumulator提供了自定义的函数操作,其构造函数如下: ```java /** * accumulatorFunction:需要执行的二元函数(接收2个long作为形参,并返回1个long) * identity:初始值 **/ public LongAccumulator(LongBinaryOperator accumulatorFunction, long identity) { this.function = accumulatorFunction; base = this.identity = identity; } ``` 示例代码: ```java package com.itsoku.chat32; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.LongAccumulator; import java.util.concurrent.atomic.LongAdder; /** * 跟着阿里p7学并发,微信公众号:javacode2018 */ public class Demo4 { static LongAccumulator count = new LongAccumulator((x, y) -> x + y, 0L); public static void incr() { count.accumulate(1); } public static void main(String[] args) throws ExecutionException, InterruptedException { for (int i = 0; i < 10; i++) { count.reset(); m1(); } } private static void m1() throws ExecutionException, InterruptedException { long t1 = System.currentTimeMillis(); int threadCount = 50; CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { new Thread(() -> { try { for (int j = 0; j < 1000000; j++) { incr(); } } finally { countDownLatch.countDown(); } }).start(); } countDownLatch.await(); long t2 = System.currentTimeMillis(); System.out.println(String.format("结果:%s,耗时(ms):%s", count.longValue(), (t2 - t1))); } } ``` 输出: ```java 结果:50000000,耗时(ms):138 结果:50000000,耗时(ms):111 结果:50000000,耗时(ms):111 结果:50000000,耗时(ms):103 结果:50000000,耗时(ms):103 结果:50000000,耗时(ms):105 结果:50000000,耗时(ms):101 结果:50000000,耗时(ms):106 结果:50000000,耗时(ms):102 结果:50000000,耗时(ms):103 ``` **平均耗时:100毫秒** `LongAccumulator`的效率和`LongAdder`差不多,不过更灵活一些。 调用`new LongAdder()`等价于`new LongAccumulator((x, y) -> x + y, 0L)`。 从上面4个示例的结果来看,`LongAdder、LongAccumulator`全面超越同步锁及`AtomicLong`的方式,建议在使用`AtomicLong`的地方可以直接替换为`LongAdder、LongAccumulator`,吞吐量更高一些。 <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container