共计 3864 个字符,预计需要花费 10 分钟才能阅读完成。
面试连环炮
先来一连炮简略的面试,看你能顶住几轮?
栈长:
1、多线程状况下,进行数字累加(count++)要留神什么?
张三:
要留神给累加办法加同步锁,不然会呈现变量可见性问题,变量值被其余线程笼罩呈现不统一的状况
栈长:
2、保障变量可见性,用 volatile 润饰不就行了吗?
张三:
volatile 是能够保障可见性,但不能保障原子性和线程平安
栈长:
3、除了加同步锁这种计划,还有别的办法吗?
张三:
还能够用 JDK 中的原子类,比方:AtomicInteger、AtomicLong,它们是通过 CAS 算法实现的一种乐观锁
栈长:
4、不错,还晓得别的么?
张三:
呃……
认真的,你能顶住几轮?
这些问题是 Java 程序员面试过程中必问的,出场率贼高,Java 程序员必懂,这些题在 Java 面试库小程序中也都有具体答案,这里就不开展了。
你还晓得别的么?最初一轮的答案就是明天的主题!
更好的抉择:LongAdder
你还在用 AtomicInteger、AtomicLong 原子类进行并发累加操作吗?那你就 OUT 了!
除了 AtomicInteger、AtomicLong,其实在 JDK 8 中更倡议应用 LongAdder 进行原子性操作,性能更好,如果你应用的还是 JDK 7-,那当我没说,即使如此,也不能找借口不晓得,毕竟 JDK 8 是当初的支流利用版本了。
阿里巴巴最新的 Java 开发手册 是这么定义的:
这份阿里巴巴残缺的 Java 开发手册,能够关注公众号:Java 核心技术,回复:手册,即可下载高清完整版。
如果你还没有用过 LongAdder,无妨看看本文,刷新你的认知,栈长带你涨常识!
为什么搞出了 LongAdder?
咱们都晓得在 JDK 5 中搞出了 AtomicInteger、AtomicLong 等原子类,这也是在 JDK 8 之前广泛用的原子性操作类,来看下 AtomicLong 的累加源码:
大家都晓得这些原子类都是通过 CAS 算法实现的乐观锁,通过旧值和现有的值一直循环比对,直到比对胜利才批改胜利完结循环。
这样就会有一个问题,如果并发数很高的话,就会造成过多的没有必要的 “循环“,这势必会影响 CPU 的性能。
所以,JDK 8 又搞进去了一个 LongAdder,也在 atomic 包下:
大家能够看到,在同级包中还有一个 LongAccumulator 类,这个这篇不开展,栈长下次再另开一篇具体分析,关注公众号:Java 技术栈,写完我会第一工夫进行推送。
LongAdder 为什么性能更好?
来剖析下 LongAdder 类的源码:
在 LongAdder 中保护了一个 Cell 数组,当 Cell 它不为空时,size 是 2 的次幂大小,每个 Cell 数组外面都有一个初始值为 0 的 long 变量,用来存储每个 Cell 的值:
而后其中的 sum 办法用来对 Cell 数组进行求和再加上 base 根底值进行返回:
对于 base 根底值:
LongAdder 并不会一开始就创立 Cell 数组,其自身也会保护一个 base 根底值,当 CAS 更新失败时才进行创立或者扩容。
来看下 AtomicXXX 和 LongAdder 更新比照图:
Cell 数组相当于一个分段的概念,把 AtomicXXX 中的一个值分成了多个值进行治理,当 CAS 更新失败时不再以后循环重试,而是尝试获取其余的资源锁,这样就升高了对于 AtomicXXX 中的单个资源的竞争,所以 LongAdder 的性能更高。
尽管 LongAdder 性能更好,那有没有毛病呢?
LongAdder 带来了良好的性能,代价必定也是有的,既然保护了 Cell 数组,也就意味着要占用更多的内存空间,以空间换工夫,也是值得的。
实战测试
既然官网都说在高并发的状况下性能更好,事实是否如此呢?
栈长必须实战测试一翻,打消大家的疑虑!
AtomicLong 测试代码:
/**
* @author: 栈长
* @from: 公众号 Java 技术栈
*/
private static void atomicLongTest() throws InterruptedException {long start = System.currentTimeMillis();
ExecutorService es = Executors.newFixedThreadPool(MAX_POOL_SIZE);
for (int i = 0; i < MAX_POOL_SIZE; i++) {es.execute(() -> {for (int j = 0; j < MAX_LOOP_SIZE; j++) {atomicLong.incrementAndGet();
}
});
}
es.shutdown();
es.awaitTermination(5, TimeUnit.MINUTES);
System.out.printf("AtomicLong %s*%s 后果:%s,耗时:%sms.\n",
MAX_POOL_SIZE,
MAX_LOOP_SIZE,
atomicLong.get(),
(System.currentTimeMillis() - start));
}
LongAdder 测试代码:
/**
* @author: 栈长
* @from: 公众号 Java 技术栈
*/
private static void longAdderTest() throws InterruptedException {long start = System.currentTimeMillis();
ExecutorService es = Executors.newFixedThreadPool(MAX_POOL_SIZE);
for (int i = 0; i < MAX_POOL_SIZE; i++) {es.execute(() -> {for (int j = 0; j < MAX_LOOP_SIZE; j++) {longAdder.increment();
}
});
}
es.shutdown();
es.awaitTermination(5, TimeUnit.MINUTES);
System.out.printf("LongAdder %s*%s 后果:%s,耗时:%sms.\n",
MAX_POOL_SIZE,
MAX_LOOP_SIZE,
longAdder.sum(),
(System.currentTimeMillis() - start));
}
这里只贴外围测试代码了,残缺代码已上传到了 Github:
https://github.com/javastacks…
测试后果:
这里测试的只有是 1 个线程 ,每个线程循环 累加 1 次,这个没有锁竞争、没有高并发操作的场景就能看出性能上的差别了。。
栈长再一直晋升 线程数、循环累加次数,失去了以下测试后果:
线程数 * 循环次数 | AtomicLong | LongAdder |
---|---|---|
1 * 1 | 45ms | 1ms |
10 * 10 | 55ms | 2ms |
10 * 100 | 56ms | 2ms |
100 * 10 | 58ms | 10ms |
100 * 100 | 74ms | 10ms |
1000 * 10 | 190ms | 71ms |
1000 * 100 | 217ms | 73ms |
1000 * 1000 | 194ms | 81ms |
1000 * 10000 | 301ms | 114ms |
1000 * 100000 | 1813ms | 277ms |
1000 * 1000000 | 17596ms | 1629ms |
图表比照:
从测试后果能够看出,LongAdder 的性能都是碾压 AtomicLong 的,最高可达 28 多倍的差距(56/2),能够说在高性能要求的高并发场景,必定是有必要用 LongAdder 的,这也是阿里巴巴为什么倡议应用 LongAdder 的起因。
当然,这只是我集体的测试,这个也和硬件配置有关系的,但毋庸置疑是,AtomicLong 的性能是更好的。另外,栈长写的 Java 多线程系列教程全副整顿好了,微信搜寻 Java 技术栈,在后盾发送:Java,能够在线浏览。
总结
本文以一场面试连环炮揭开了 LongAdder 的面纱,怎么解决 count++ 的线程安全性问题?
栈长再总结下:
- 累加办法加 synchronized/ Lock 同步锁;
- 应用 AtomicInteger/ AtomicLong 原子类;
- 应用 LongAdder 原子类(举荐应用);
LongAdder 这个货色是 Java 8 搞进去的,用来代替 AtomicXXX,不论是否高并发场景,都完胜 AtomicXXX,它不仅能够改善性能,当初面试也问的越来越多了,大家还是有必要把握。
尽管 LongAdder 性能更好,但也是以更多的内存空间耗费为代价的,当然,古代计算机,内存早已不是瓶颈,所以这点耗费是能够忽略不计的,性能还是最重要的,然而大家也要晓得这个点。
本文实战源代码完整版曾经上传:
https://github.com/javastacks…
欢送 Star 学习,公众号所有 Java 实战示例都会在这下面提供!
好了,明天的分享就到这里了,前面栈长会分享更多好玩的 Java 技术和最新的技术资讯,关注公众号 Java 技术栈第一工夫推送,我也将支流 Java 面试题和参考答案都整顿好了,在公众号后盾回复关键字 “ 面试 ” 进行刷题。
最初,感觉我的文章对你用播种的话,动动小手,给个在看、转发,原创不易,栈长须要你的激励。
版权申明: 本文系公众号 “Java 技术栈 ” 原创,转载、援用本文内容请注明出处,剽窃、洗稿一律投诉侵权,后果自负,并保留追究其法律责任的权力。
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿(2022 最新版)
2. 劲爆!Java 协程要来了。。。
3.Spring Boot 2.x 教程,太全了!
4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!