Java的Atomic原子类
Java SDK 并发包里提供了丰富的原子类,我们可以将其分为五个类别,这五个类别提供的方法基本上是相似的,并且每个类别都有若干原子类。
- 对基本数据类型的变量值进行原子更新;
- 对对象变量的指向进行原子更新;
- 对数组里面的的元素进行原子更新;
- 原子化的对象属性更新器;
- 原子化的累加器。

基本数据类型
AtomicBoolean、AtomicLong、AtomicInteger 这三个类提供了一些对基本数据类型的变量值进行原子更新的方法。
这些类提供的方法是相似的,主要有(以 AtomicLong 为例):
// 原子化的 i++
long getAndIncrement()
// 原子化的 i--
long getAndDecrement()
// 原子化的 ++i
long incrementAndGet()
// 原子化的 --i
long decrementAndGet()
// 原子化的 i+=delta,返回值为+=前的i值
long getAndAdd(long delta)
// 原子化的 i+=delta,返回值为+=后的i值
long addAndGet(delta)
// CAS操作。如果写回成功返回true,否则返回false
boolean compareAndSet(long expect, long update)
// 以下四个方法新值可以通过传入函数式接口(func函数)来计算
long getAndUpdate(LongUnaryOperator updateFunction)
long updateAndGet(LongUnaryOperator updateFunction)
long getAndAccumulate(long x, LongBinaryOperator accumulatorFunction)
long accumulateAndGet(long x, LongBinaryOperator accumulatorFunction)
// 演示 getAndUpdate() 方法的使用
public static void main(String[] args) {
AtomicLong atomicLong = new AtomicLong(0);
long result = atomicLong.getAndUpdate(new LongUnaryOperator() {
@Override
public long applyAsLong(long operand) {
return operand + 1;
}
});
}
对象引用类型
AtomicReference、AtomicStampedReference、AtomicMarkableReference 这三个类提供了一些对对象变量的指向进行原子更新的方法。如果需要对对象的属性进行原子更像,那么可以使用原子化的对象属性更新器。
public class ClassName {
AtomicReference<Employee> employeeAR = new AtomicReference<>(new Employee("小明"));
public void methodName() {
Employee oldVal = employeeAR.get();
Employee newVal = new Employee(oldVal.getName());
employeeAR.compareAndSet(oldVal, newVal);
}
}
对象引用的原子化更新需要重点关注 ABA 问题。当一个线程在进行 CAS 操作时,另一个线程可能会在此期间修改了同一个共享变量的值,然后又将其改回原来的值。这种情况下,CAS 操作就无法检测到共享变量值的变化,从而导致 ABA 问题。如果我们仅仅在写回数据前判断数值是 A,可能导致不合理的写回操作。AtomicStampedReference 和 AtomicMarkableReference 这两个原子类可以解决 ABA 问题。
- AtomicStampedReference 通过为对象引用建立类似版本号(stamp)的方式,来解决 ABA 问题。AtomicStampedReference 实现的 CAS 方法增加了版本号参数
- AtomicMarkableReference 的实现机制则更简单,将版本号简化成了一个 Boolean 值
boolean compareAndSet(V expectedReference, V newReference,
int expectedStamp, int newStamp)
boolean compareAndSet(V expectedReference, V newReference,
boolean expectedMark, boolean newMark)
数组
AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray 这三个类提供了一些对数组里面的的元素进行原子更新的方法。
public class ClassName {
AtomicLongArray atomicLongArray = new AtomicLongArray(new long[]{0, 1});
public void methodName() {
int index = 0;
long oldVal = atomicLongArray.get(index);
long newVal = oldVal + 1;
atomicLongArray.compareAndSet(index, oldVal, newVal);
}
}
原子化的对象属性更新器
原子化的对象属性更新器有:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater。
这三个类提供了一些对对象的属性进行原子更新的方法。这些方法是利用反射机制实现的。
public class ClassName {
AtomicIntegerFieldUpdater<Employee> fieldUpdater =
AtomicIntegerFieldUpdater.newUpdater(Employee.class, "salary");
Employee employee = new Employee("小明", 1000);
public void methodName() {
int oldVal = employee.getSalary();
int newVal = oldVal + 1000;
fieldUpdater.compareAndSet(employee, oldVal, newVal);
}
}
需要注意的是:
- 对象属性的类型必须是基本数据类型,不能是基本数据类型对应的包装类。如果对象属性的类型不是基本数据类型,newUpdater() 方法会抛出 IllegalArgumentException 运行时异常。
- 对象的属性必须是 volatile 类型的,只有这样才能保证可见性。如果对象的属性不是 volatile 类型的,newUpdater() 方法会抛出 IllegalArgumentException 运行时异常。
// AtomicIntegerFieldUpdater 类中的代码
if (field.getType() != int.class) {
throw new IllegalArgumentException("Must be integer type");
}
if (!Modifier.isVolatile(modifiers)) {
throw new IllegalArgumentException("Must be volatile type");
}
原子化的累加器
原子化的累加器有:LongAdder、DoubleAdder、LongAccumulator、DoubleAccumulator。这四个类仅仅用来在多线程环境下,执行累加操作。
相比原子化的基本数据类型,原子化的累加器的速度更快,但是它(原子化的累加器)不支持 compareAndSet() 方法。如果仅仅需要累加操作,使用原子化的累加器性能会更好。
原子化的累加器的本质是空间换时间。
LongAdder 的使用示例如下所示:
public static void main(String[] args) {
LongAdder adder = new LongAdder();
// 初始化
adder.add(1);
// 累加
for (int i = 0; i < 100; i++) {
adder.increment();
}
long sum = adder.sum();
}
LongAccumulator 与 LongAdder 类似,但 LongAccumulator 提供了更加灵活的累加操作,可以自定义累加函数。
使用示例如下所示。在使用示例中,我们创建了一个 LongAccumulator 对象,初始值为1,累加函数为 (x, y) -> x * y,即每次累加都将之前的结果与新的值相乘。然后,我们累加了三个数值,最后输出累加结果。由于累加函数是(x, y) -> x * y,所以最终的累加结果为1 * 5 * 10 * 20 = 1000。
public static void main(String[] args) {
LongAccumulator accumulator = new LongAccumulator(new LongBinaryOperator() {
@Override
public long applyAsLong(long left, long right) {
return left * right;
}
}, 1);
// 初始值为1,累加函数为(x, y) -> x * y
accumulator.accumulate(5);
accumulator.accumulate(10);
accumulator.accumulate(20);
// 累加结果为 1 * 5 * 10 * 20 = 1000
long result = accumulator.get();
}
参考资料
21 | 原子类:无锁工具类的典范 (geekbang.org)
Java的Atomic原子类的更多相关文章
- Java线程--Atomic原子类使用
原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11871241.html Java线程--Atomic原子类使用 package concurr ...
- Java CAS同步机制 原理详解(为什么并发环境下的COUNT自增操作不安全): Atomic原子类底层用的不是传统意义的锁机制,而是无锁化的CAS机制,通过CAS机制保证多线程修改一个数值的安全性。
精彩理解: https://www.jianshu.com/p/21be831e851e ; https://blog.csdn.net/heyutao007/article/details/19 ...
- (转)Java atomic原子类的使用方法和原理(一)
在讲atomic原子类之前先看一个小例子: public class UseAtomic { public static void main(String[] args) { AtomicIntege ...
- Java多线程系列——原子类的实现(CAS算法)
1.什么是CAS? CAS:Compare and Swap,即比较再交换. jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronou ...
- JUC 中的 Atomic 原子类总结
1 Atomic 原子类介绍 Atomic 翻译成中文是原子的意思.在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的.在我们这里 Atomic 是指一个操作是不可中断的.即使是 ...
- Atomic原子类
Atomic原子类 Atomic原子类位于并发包java.util.concurrent下的java.util.concurrent.Atomic中. 1. 原子更新基本类型类 使用原子方式更新基本数 ...
- Juc中Atomic原子类总结
1 Atomic原子类介绍 2 基本类型原子类 3 数组类型原子类 4 引用类型原子类 5 对象的属性修改类型原子类
- [Java多线程]-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)
前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...
- 深入浅出Java并发包—原子类操作
我们知道,JDK1.5以后引入了并发包(java.util.concurrent)用于解决多CPU时代的并发问题,而并发包中的类大部分是基于Queue的并发类,Queue在大多数情况下使用了原子类(A ...
随机推荐
- mybatis-plus 开发环境在控制台打印日志
参考博客:https://blog.csdn.net/qq_32929057/article/details/109291919 # 注意在生产环境注释掉 mubatis-plus: configur ...
- OVS内核流表查询过程
概括 现在的OVS使用microflow+megaflow缓存查询流表,ovs整体流程是从ovs_vport_receive(datapath/vport.c)开始,然后进入ovs_dp_proces ...
- 2020 Multi-University Training Contest 1 . Fibonacci Sum 水题改编
题意很简单,就是让你求这个东西,这个时候你发现,原题???? https://blog.csdn.net/acdreamers/article/details/23039571 哦,只是原来写过的哪一 ...
- BeautifulSoup模块的使用方法
本篇文章主要讲bs4模块(BeautifulSoup),这个模块能做么呢?用一句话来概括的话:beautifulsoup4 从HTML或XML文件中提取数据的Python库,用它来解析爬取回来的xml ...
- MyBatis中的#和$有什么区别
什么是MyBatis MyBatis是一款优秀的持久层框架,特别是在国内(国外据说还是 Hibernate 的天下)非常的流行,我们常说的SSM组合中的M指的就是#mybatis#. MyBatis支 ...
- PHP 微信三方平台代公众号发起网页授权 获取用户信息
1.获取code 2.通过授权回调地址的code获取用户access_token和open_id 3.通过access_token和open_id 获取用户基本信息 class wx_user { p ...
- STM32 HAL库学习(F407ZGT6) (1)-晶振/时钟树
时钟树(以F407为例) 对于 STM32F4 系列的芯片,正常工作的主频可以达到 168Mhz,但并不是所有外设都需要系统时钟这么高的频率,比如看门狗以及RTC只需要几十Khz的时钟即可.同一个 ...
- ArcGIS Pro处理发布并在前端调用bim数据全过程-rvt转slpk
记录ArcGIS处理三维bim模型全纪录,从原始的rvt格式开始,到最后web前端js api调用的整个过程,并记录部分中间操作过程中出现的问题和解决办法. 本文示例使用: 软件:ArcGIS Pro ...
- 快速使用ChatGpt Web Server
快速使用ChatGpt Web Server ChatGpt Web Server是使用Blazor Server模式部署的一个服务,所有的逻辑和代码执行都会在服务器执行,然后通过SignalR传输到 ...
- 标准正态分布表—R语言
正态分布是最重要的一种概率分布.正态分布概念是由德国的数学家和天文学家Moivre于1733年首次提出的,但由于德国数学家Gauss率先将其应用于天文学家研究,故正态分布又叫高斯分布.高斯这项工作对后 ...