【连载 09】atomic 包原子类
2.4 atomic 包原子类
java.util.concurrent.atomic 包提供了一组用于实现原子操作的类。这些类可以用于在多线程环境中执行线程安全的、不可中断的原子操作。原子操作类提供了一种稳定可靠的方式执行原子类操作,而不是使用锁实现。原子操作类适用于各种并发场景,特别在是高并发场景种,原子操作类性能表现非常突出。
atomic 包下面的功能类,性能测试使用到的只有 4 种:分别是 AtomicBoolean、AtomicInteger、AtomicLong 和 LongAdder。
对于前三种,相信你会有一种眼熟的感觉,都有对应的 Java 基础数据类型,使用方式也很相似,把 Java 操作符换成了方法实现取值和赋值功能。下面逐个介绍几位主角。
1. AtomicBoolean
AtomicBoolean 类常用于多线程环境种对 Boolean 值的操作。使用者可以直接对 AtomicBoolean 对象的值进行修改,而不需要借助锁和 synchronized 关键字。下面是 AtomicBoolean 常用的功能。
public AtomicBoolean(boolean initialValue):构造方法,有无参构造方法,等效于该方法 initialValue = false。
get():获取 Boolean 值的方法,返回 boolean 值。
set(boolean newValue):设置新的 boolean 值。
getAndSet(boolean newValue):获取当前值,并设置新的值。
compareAndSet(boolean expect, boolean update):若当前值等于预期,则更新值,返回 true。否则不更新,返回 false。
AtomicBoolean 在性能测试中,通常用来标记线程状态,控制线程启动和停止;资源初始化、重置的状态标记,减少线程间竞争;还可以用来控制并发,限制同时执行某个操作的线程数。
2. AtomicInteger
AtomicInteger 类常用于对整数值的执行原子操作。使用者可以对 AtomicInteger 对象进行线程安全的加、减操作,同样的不需要锁和 synchronized。下面是 AtomicInteger 常用功能。
public AtomicInteger(int initialValue):构造方法,有无参构造方法,等效于该方法 initialValue = 0。
get():获取整数值的方法,返回整数值。
set(int newValue):设置整数值。
getAndSet(int newValue):获取当前值,并重新新的值。
getAndIncrement() 和 getAndDecrement():获取当前值,并且对当前值进行加一和减一操作。同胞兄弟 incrementAndGet()和 decrementAndGet(),区别在于这两个方法先进行赋值操作后获取值,类似与 i++ 和 ++i 的区别。
addAndGet(int delta):在当前值基础上增加指定量(可以是负值),并获取新值。
compareAndSet(int expect, int update):若当前值等于预期值,则重新赋值,返回 true,否则不更新值,返回 false。
AtomicInteger 主要应用在多线程场景中,计数和状态控制。在性能测试中,通常用来进行数据的计算,例如统计执行任务的数量、用户对某商品的消费总额等。
3. AtomicLong
AtomicLong 功能跟 AtomicInteger 一样,区别在于 AtomicLong 可以表达的整数类型范围更广。两者所能处理的整数范围预期对应基础数据类型所能表示整数类型范围一样。
4. LongAdder
LongAdder 功能与 AtomicLong、AtomicInteger 一模一样,能处理整数值范围与 AtomicLong 一致。区别在于 LongAdder 在高并发计数场景中,性能表现更好。原因在于 LongAdder 使用了分段锁来降低线程竞争,这个设计思路跟 ConcurrentHashMap 一致。如果你将来遇到类似场景,可以阅读这两个类的源码寻找设计灵感。下面介绍一下 LongAdder 的常用功能。
LongAdder():构造方法,只此一家,默认值为 0。
add(long x):将当前值增加固定量(可以为负)。
increment()和 decrement():将当前值自增一和自减一,等效于 add(1L)和 add(-1L)。
sum():获取当前累计值,由于采取了分段设计,所以方法名是求和(sum)。
reset():重置当前对象,回复默认值 0。
sumThenReset():获取当前值,并重置。
LongAdder 使用场景跟前两者基本重合,如果你需要进行高并发计数、大规模数据聚合操作,优先考虑 LongAdder 实现该功能。
对于高并发并没有一个严格的规范,笔者提供一个数据仅供参考:在 500 线程并发场景下:LongAdder 优势比较明显;在 200 ~ 300 并发场景中,LongAdder 略有优势;在更低的并发中,两者性能无明显差异。
如果在工作中,你仍然无法决断,后面会讲到 Java 微基准测试工具 JMH,可以帮助你在实际场景中做出明智的选择。
书的名字:从 Java 开始做性能测试 。
如果本书内容对你有所帮助,希望各位不吝赞赏,让我可以贴补家用。赞赏两位数可以提前阅读未公开章节。我也会尝试制作本书的视频教程,包括必要的答疑。
版权声明: 本文为 InfoQ 作者【FunTester】的原创文章。
原文链接:【http://xie.infoq.cn/article/4998fc9be54d9e33b311ed7b3】。文章转载请联系作者。
评论