java多线程之AtomicLong原子类(JDK8)

您所在的位置:网站首页 atomic可见性 java多线程之AtomicLong原子类(JDK8)

java多线程之AtomicLong原子类(JDK8)

2023-08-22 15:24| 来源: 网络整理| 查看: 265

引言

有的同学可能会问这个问题,有了Long类型,为什么还要弄一个AtomicLong类出来?因为在32位的操作系统中,64位的Long类型变量会被jvm拆分为两个32位的来操作,因此不具备原子性。而AtomicLong类型可以保证原子性。

1、AtomicLong介绍

AtomicInteger, AtomicLong和AtomicBoolean这3个基本类型的原子类的原理和用法相似。本章以JDK8为版本对AtomicLong基本类型的原子类进行介绍。AtomicLong继承Number抽象类,实现了Serializable接口,操作是线程安全的。

2、分析源码 // 构造函数 AtomicLong() // 创建值为initialValue的AtomicLong对象 AtomicLong(long initialValue) // 以原子方式设置当前值为newValue。 final void set(long newValue) // 获取当前值 final long get() // 以原子方式将当前值减 1,并返回减1后的值。等价于“--num” final long decrementAndGet() // 以原子方式将当前值减 1,并返回减1前的值。等价于“num--” final long getAndDecrement() // 以原子方式将当前值加 1,并返回加1后的值。等价于“++num” final long incrementAndGet() // 以原子方式将当前值加 1,并返回加1前的值。等价于“num++” final long getAndIncrement() // 以原子方式将delta与当前值相加,并返回相加后的值。 final long addAndGet(long delta) // 以原子方式将delta添加到当前值,并返回相加前的值。 final long getAndAdd(long delta) // 如果当前值 == expect,则以原子方式将该值设置为update。成功返回true,否则返回false,并且不修改原值。 final boolean compareAndSet(long expect, long update) // 以原子方式设置当前值为newValue,并返回旧值。 final long getAndSet(long newValue) //以下部分省略……

AtomicLong的方法都比较简单,下面重点分析下incrementAndGet的方法

public final long incrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L; } //unsafe中getAndAddLong实现 public final long getAndAddLong(Object var1, long var2, long var4) { long var6; do { //这个this表示了当前对象的地址,后面的valueOffset是在该对象开始的位置加上这个valueOffset的偏移量,就能拿到的是当前对象的值 var6 = this.getLongVolatile(var1, var2); } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4)); return var6; }

在getAndAddLong实现中,compareAndSwapLong基于的是CPU 的 CAS指令来实现的,可认为是无阻塞的,一个线程的失败或挂起不会引起其它线程也失败或挂起。但并发量很大的话,cpu会花费大量的时间在试错上面,相当于一个自旋的操作。如果并发量小的情况,这些消耗可以忽略不计。 JDK8中新增了LongAdder,内部的实现有点类似ConcurrentHashMap的分段锁,最好的情况下,每个线程都有独立的计数器,这样可以大量减少并发操作。

3、性能测试

下面通过JMH比较一下AtomicLong 和 LongAdder的性能

@OutputTimeUnit(TimeUnit.MICROSECONDS) @BenchmarkMode(Mode.AverageTime) public class AtomicLongTest { private static AtomicLong count = new AtomicLong(); private static LongAdder longAdder = new LongAdder(); public static void main(String[] args) throws Exception { Options options = new OptionsBuilder().include(AtomicLongTest.class.getName()).forks(1).build(); new Runner(options).run(); } @Benchmark @Threads(10) public void run0(){ count.getAndIncrement(); } @Benchmark @Threads(10) public void run1(){ longAdder.increment(); } }

maven的pom文件依赖包

org.openjdk.jmh jmh-core 1.21 org.openjdk.jmh jmh-generator-annprocess 1.21

1、设置BenchmarkMode为Mode.Throughput,测试吞吐量 2、设置BenchmarkMode为Mode.AverageTime,测试平均耗时 3、程序运行直接使用run方法,不支持debug模式

下面是运行的结果: 吞吐量

Benchmark Mode Cnt Score Error Units AtomicLongTest.run0 thrpt 5 34.070 ± 1.830 ops/us AtomicLongTest.run1 thrpt 5 152.216 ± 45.756 ops/us

平均耗时

Benchmark Mode Cnt Score Error Units AtomicLongTest.run0 avgt 5 0.350 ± 0.063 us/op AtomicLongTest.run1 avgt 5 0.072 ± 0.029 us/op

从上面可以看出LongAdder的吞吐量和平均耗时均优于AtomicLong

4、总结

普通场景保证线程安全,建议使用AtomicLong,一些高并发的场景,比如限流计数器,建议使用LongAdder替换AtomicLong,以提高性能。

结束语

本篇介绍了AtomicLong的基本原理,比较了LongAdder和AtomicLong之间高并发下的性能,还介绍了一款适合java的JMH性能测试工具,有兴趣的同学可以详细的了解下,下一章将介绍JUC包中的锁。

如果你觉得本篇文章对你有帮助的话,请帮忙点个赞,再加一个关注。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3