Java的Atomic原子类

news2024/12/26 3:09:21

Java SDK 并发包里提供了丰富的原子类,我们可以将其分为五个类别,这五个类别提供的方法基本上是相似的,并且每个类别都有若干原子类。

  • 对基本数据类型的变量值进行原子更新;
  • 对对象变量的指向进行原子更新;
  • 对数组里面的的元素进行原子更新;
  • 原子化的对象属性更新器;
  • 原子化的累加器。

007a32583fbf519469462fe61805eb4a.png

基本数据类型

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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/571933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何使用 Fail2ban 防止对 Linux 的暴力攻击?

在当今数字化世界中&#xff0c;网络安全成为了一个极其重要的话题。Linux 作为一种广泛使用的操作系统&#xff0c;也面临着各种网络攻击的风险&#xff0c;包括暴力攻击、密码破解和恶意登录等。为了保护 Linux 系统的安全&#xff0c;我们可以使用 Fail2ban 这样的工具来防止…

什么是日志关联

什么是日志关联 日志关联是一种分析来自不同源的日志数据以识别事件模式的技术。它用于更好地了解网络的活动&#xff0c;从而有效地保护网络免受漏洞和威胁。 日志关联是日志管理过程的关键部分。收集和存储日志后&#xff0c;集中式日志服务器将执行分析以检测特定事件。日…

Java Web 编写第一个Servlet程序全过程

一、工具准备 IDEATomcat 二、创建一个名为hello-servlet的maven项目 三、在工程根目录下创建一个web文件夹&#xff0c;web文件夹下创建WEB-INF目录&#xff0c;WEB-INF目录下创建web.xml文件&#xff0c;目录结构以及web.xml文件内容如下&#xff1a; <?xml version&qu…

AcrelEMS-HIM高速公路综合能效系统在新晋高速公路快村营至营盘段项目的应用

​安科瑞 耿敏花 摘 要&#xff1a;我国新型工业化、信息化、城镇化和农业现代化加快发展&#xff0c;经济结构加快转型&#xff0c;交通运输总量将保持较快增长态势&#xff0c;各项事业发展要求提高国家公路网的服务能力和水平。高速公路沿线的收费站、互通枢纽、服务区、隧道…

三年外包的血泪史,终于脱离苦海了~

外包给的不高&#xff0c;是真的不能去呀&#xff01; 先说一下我的个人情况&#xff0c;大专生&#xff0c;18年通过校招进入湖南某软件公司&#xff0c;干了接近3年的功能测试点点点&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒…

C# WPF窗体设计器显示以及App.xaml文件打不开(VS 2022)

问题描述&#xff1a; 在项目中遇到了App.xaml设计器打不开以及窗体设计器不显示&#xff0c;只有代码&#xff0c;如图所示&#xff1a; 可以明显的看见左下角的设计器不见&#xff0c;但是用户控件又有设计器 解决方法&#xff1a; (一、App.xaml不能正常打开) ①清理项…

数字孪生智慧工厂可视化分析决策方案,打造智慧汽车工厂

智慧工厂是当前智能制造领域的热门话题之一&#xff0c;是一种集成数字技术、先进制造技术和现代管理技术的新型工厂模式。随着全球制造业的发展&#xff0c;智慧工厂逐渐成为未来工厂发展的一大趋势&#xff0c;越来越多的企业开始关注智慧工厂的建设。 该数字孪生智慧汽车工厂…

vue-cli4+vant+rem+sass+vuex+axios封装+webpack搭建前端项目

移动端项目模板 基于 vue-cli4.0 webpack 4 vant ui sass rem 适配方案axios 封装&#xff0c;构建手机端模板脚手架 启动项目 git clone https://github.com/teach-tian/h5-vue-cli4.gitcd h5-vue-cli4npm installnpm run serve✅ 配置多环境变量 package.json 里的 s…

Stimulsoft报表开发工具支持电子签名啦!一起来看

Stimulsoft Reports 是一款报告编写器&#xff0c;主要用于在桌面和Web上从头开始创建任何复杂的报告。可以在大多数平台上轻松实现部署&#xff0c;如ASP.NET, WinForms, .NET Core, JavaScript, WPF, Angular, Blazor, PHP, Java等&#xff0c;在你的应用程序中嵌入报告设计器…

java 注解学习

Java 语言中存在三类注解&#xff0c;分别是元注解&#xff08;Meta-annotations&#xff09;、Java 内置注解&#xff08;Built-in Annotations&#xff09;和自定义注解&#xff08;Custom Annotations&#xff09;。 1、元注解&#xff08;Meta-annotations&#xff09; 元…

Pandas-如何对指定某列的NaN值进行替换或填充

前言 本文是该专栏的第31篇,后面会持续分享python数据分析的干货知识,记得关注。 笔者在本专栏之前有单独详细介绍过,使用Numpy对数组元素进行替换的方法,感兴趣的同学,可翻阅查看“Numpy-如何对数组的元素进行替换”。 而本文来单独介绍pandas对指定列的NaN值进行操作的…

《汇编语言》- 读书笔记 - 实验5 编写、调试具有多个段的程序

《汇编语言》- 读书笔记 - 实验5 编写、调试具有多个段的程序 题目1题目2题目3题目4题目5题目6总结 题目1 将下面的程序编译、连接&#xff0c;用 Debug 加载、跟踪&#xff0c;然后回答问题 assume cs:code, ds:data, ss:stack data segmentdw 0123h,0456h,0789h,0abch,0def…

github添加ssh-key来支持git项目管理

背景 https://github.com很多时候无法克隆/更新/提交项目&#xff0c;使用gitgithub.com怎没有限制 配置git账户邮箱和用户名 查看配置信息 git config --global --list 配置或者修改用户名&#xff0c;替换为自己github用户名 git config --global user.name "holyl…

【什么是苹果IM推?什么是苹果推?】iMessage推送操纵Apple Push Notification service (APNs)

以帮忙你明白实现iMessage推送的基本原理和步调&#xff1a; 开辟者账户&#xff1a;确保你具有苹果开发者账户&#xff0c;以便访谒苹果开发者中间和相干东西。 APNs认证&#xff1a;iMessage推送操纵Apple Push Notification service (APNs)来发送关照。在苹果开发者中心&a…

Spring支持哪些Aware接口?

Spring支持哪些Aware接口? Spring支持哪些Aware接口?Aware接口的优点ApplicationContextAware和BeanFactoryAware的区别 Spring支持哪些Aware接口? ApplicationContextAware:获取ApplicationContext对象BeanFactoryAware:获取BeanFactory对象BeanNameAware:获取Bean的名称E…

Fiddler抓取HTTPS最“全”攻略,让你成为网络调试大师!

对于想抓取HTTPS的测试初学者来说&#xff0c;常用的工具就是fiddler。 在使用Fiddler进行HTTPS抓包时&#xff0c;很多人都会遇到各种各样的问题和困难。 初学时&#xff0c;大家对于fiddler如何抓取HTTPS难免走歪路&#xff0c;也许你一步步按着网上的帖子成功了&#xff0…

Kubernetes 准入控制器

Kubernetes 极大地提高了当今生产中后端集群的速度和可管理性。由于灵活、可扩展、易用&#xff0c;Kubernetes 已成为容器编排的事实标准。Kubernetes 还提供了一系列保护功能。而 Admission Controllers&#xff08;准入控制器&#xff09; 是一组安全相关的插件&#xff0c;…

Failed to start application ‘/LM/W3SVC/7/ROOT‘, ErrorCode ‘0x800700c1‘.解决方案

dll相互干扰所致 关闭整个IIS服务 发布选项勾选删除现有文件 即可

Seata AT模式源码解析二(Seata Client端启动流程)

文章目录 初始化TM和RM数据源代理 由于我们一般都是在springboot中使用的&#xff0c;而与springboot集成的我们一般就先看starter的spring.factories文件&#xff0c;看看它的自动装配 这里面主要关注SeataAutoConfiguration和SeataDataSourceAutoConfiguration。 SeataAutoCo…

C# WPF窗体设计器显示以及App.xaml文件打不开

问题描述&#xff1a; 在项目中遇到了App.xaml设计器打不开以及窗体设计器不显示&#xff0c;只有代码&#xff0c;如图所示&#xff1a; 可以明显的看见左下角的设计器不见&#xff0c;但是用户控件又有设计器 解决方法&#xff1a; ①清理项目 ②将不能正常打开的文件右…