JUC并发编程 08——原子操作类

目录

一.原子更新基本类型类

实现原理

二.原子更新数组

三.原子更新引用类型

四.原子更新字段类


Java从JDK1.5开始提供了J.U.C下的atomic包,atomic包提供了一系列的操作简单,性能高效,并能保证线程安全的类去更新基本类型变量,数组元素,引用类型以及更新对象中的字段类型。

atomic包里的类基本都是使用 Unsafe 实现的包装类,使用Unsafe提供的三个CAS方法结合死循环实现的,也就是乐观锁。

一.原子更新基本类型类

我们Java中的基本数据类型是用的非常多的,Atomic包针对基本数据类型提供了3个类,使用原子的方式更新基本类型。

  • AtomicBoolean:原子更新布尔类型
  • AtomicInteger:原子更新整型
  • AtomicLong:原子更新长整型

以上3个类提供的方法几乎一模一样,所以这里只以AtomicInteger为例进行讲解。

  • int addAndGet(int delta):原子地将给定值与实例中的值(AtomicInteger里的value)相加,并返回结果。
  • boolean compareAndSet(int expect, int update):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值
  • int getAndIncrement():以原子的方式将当前值+1,注意,这里是返回增加之前的原有值
  • void lazySet(int newValue):懒设置,即最终会设置成newValue,使用lazySet设置值后,可能导致其他线程在之后的一小段时间内还是可以读到旧的值,这是基于性能方面考虑,因为在多个CPU缓存间同步一个内存的代价是很昂贵的。
  • int getAndSet(int newValue):以原子方式设置为newValue的值,并返回旧值

实现原理

getAndIncrement() 是如何实现原子操作的呢?让我们一起分析其实现原理。

可以看出,该方法实际上是调用了unsafe实例的getAndAddInt方法,unsafe实例的获取时通过UnSafe类的静态方法getUnsafe获取:

Unsafe 是JDK提供的一个工具类,里面有很多 native 方法,即本地方法,是 JVM 使用 C 帮我们实现的,我们只要记住他提供了一些安全的操作方法,是直接操作内存的。

unsafe.getAndAddInt(this,valueOffset,1) 方法中的三个参数:

  • 第1个 this 就是指我们当前操作的 AtomicInteger 对象,
  • 第2个参数 valueOffset,这是一个成员变量,代表value成员属性的内存地址相对于对象内存地址的偏移量。这样就能根据对象的内存地址找到属性值的内存地址。
  • 第3个参数是1,这个也好理解,因为我们这个方法就是用来自增的嘛,每次增加1,是增量值。

我们接着往里看:

这里使用了 do-while 循环,其实也就是乐观锁,非阻塞的同步方式,一直尝试进行原子操作更新,直到成功。

Atomic包提供了3种基本类型的原子更新,但是Java的基本类型里还有 char、float和double等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic包里的类基本都是使用Unsafe实现的,让我们一起看一下Unsafe的源码。

/**
* 如果当前数值是expected,则原子的将Java变量更新成x
* @return 如果更新成功则返回true
*/
public final native boolean compareAndSwapObject(Object o,long offset , Object expected , Object x );
public final native boolean compareAndSwapInt(Object o , long offset , int expected, int x );
public final native boolean compareAndSwapLong(Object o , long offset , long expected ,long x );

通过代码,我们发现Unsafe只提供了三种CAS方法:

  • boolean compareAndSwapObject(Object o,long offset , Object expected , Object x );
  • boolean compareAndSwapInt(Object o , long offset , int expected, int x );
  • boolean compareAndSwapLong(Object o , long offset , long expected ,long x );

再看 AtomicBoolean 源码

它是先把Boolean转换成整型,再使用 compareAndSwapInt 进行CAS,所以原子更新char、float和double变量也可以用类似的思路来实现。

二.原子更新数组

通过原子的方式更新数组里的某个元素,即操作数组元素是线程安全的,Atomic包提供了以下3个类:

  • AtomicIntegerArray:原子更新整型数组里的元素
  • AtomicLongArray:原子更新长整型数组里的元素
  • AtomicReferenceArray:原子更新引用类型数组里的元素

上述几个类提供的方法几乎一样,我们以AtomicIntegerArray类为例讲解,其常用方法如下:

  • int addAndGet(int i , int delta):以原子方式将输入值与数组中索引 i 的元素相加
  • boolean compareAndSet(int i , int expect , int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值

三.原子更新引用类型

上面我们学会了如何原子更新基本类型,但是我们平常使用最多的还是引用类型,这个要如何更新呢?Atomic包提供了以下3个类:

  • AtomicReference:原子更新引用类型
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段
  • AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子更新一个布尔类型的标记位和引用类型。构造方法是 AtomicMarkableReference(V initialRef,boolean initialMark)

以上几个类提供的方法几乎一样,所以此处我们仅以AtomicReference为例进行讲解,AtomicReference的使用示例代码如下:

其实现原理是依靠了 unsafe.compareAndSwapObject 方法。

四.原子更新字段类

如果需原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下3个类进行原子字段更新。

  • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器
  • AtomicLongFieldUpdater:原子更新长整型字段的更新器
  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题

要想原子地更新字段类需要两步:

  1. 第一步,因为原子更新字段类都是抽象类,每次使用的时候必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性
  2. 第二步,更新类的字段(属性)必须使用 public volatile 修饰符

以上3个类提供的方法几乎一样,此处仅以 AtomicIntegerFieldUpdater 为例进行讲解,AtomicIntegerFieldUpdater的 示例代码如下:

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数字图像处理-空间域图像增强-爆肝18小时用通俗语言进行超详细的总结

目录 灰度变换 直方图(Histogram) 直方图均衡 直方图匹配(规定化) 空间滤波 低通滤波器 高通滤波器 ​​​​​​​ 本文章讲解数字图像处理空间域图像增强,大部分内容来源于课堂笔记中 灰度变换 图像增强&…

C++的面向对象学习(4):对象的重要特性:构造函数与析构函数

文章目录 前言:将定义的类放在不同文件夹供主文件调用的方法一、构造函数与析构函数1.什么是构造函数和析构函数?2.构造函数和析构函数的语法3.构造函数的具体分类和调用方法①总的来说,构造函数分类为:默认无参构造、有参构造、拷…

【扩散模型】9、Imagen | 借用语言模型的能力来实现文生图(NIPS2022 Oral)

文章目录 一、背景二、方法2.1 预训练的语言编码器2.2 扩散模型和 classifier-free guidance 三、效果 论文:Imagen: Photorealistic Text-to-Image Diffusion Models with Deep Language Understanding 官网:https://www.assemblyai.com/blog/how-imag…

Python命名规范中的[单/双][前导/后缀]下划线小结

如图所示 出处 Single and Double Underscores in Python Names

flutter + firebase 云消息通知教程 (android-安卓、ios-苹果)

如果能看到这篇文章的 一定已经对手机端的 消息推送通知 有了一定了解。 国内安卓厂商这里不提都有自己的FCM 可自行查找。(国内因无法科学原因 ,不能使用谷歌服务)只说海外的。 目前 adnroid 和 ios 推送消息分别叫 FCM 和 APNs。这里通过…

金蝶云星空业务对象添加网控设置

文章目录 金蝶云星空业务对象添加网控设置排查是否已经网控设置网控设置 金蝶云星空业务对象添加网控设置 排查是否已经网控设置 网控设置

TrustZone之安全启动与引导失败处理

一、引导和信任链 引导是任何TrustZone系统的关键部分。只有在引导流程中之前运行的所有软件组件都是可信的情况下,才能信任某个软件组件。这通常被称为信任链。下图显示了一个简化的信任链: 在我们的示例中,首先运行的代码是boot ROM。我们必须隐式信任boot ROM,因…

JavaScript高级 函数进阶篇

函数进阶 1、函数的定义和调用 函数声明方式function关键字(命名函数);函数表达式(匿名函数);new Function()(此处的Function()是一个构造函数);var fn new Function(参…

输入框获取焦点

Entry Component struct Test {build() {Row() {Column({ space: 5 }) {//Text("自定义样式").customStyles(20,Color.Yellow).backgroundColor("#36D").padding(10).borderRadius(30)TextInput({placeholder: "获取焦点"}).borderColor(Color.Y…

只更新软件,座椅为何能获得加热功能?——一文读懂OTA

2020年,特斯拉发布过一次OTA更新,车主可以通过这次系统更新获得座椅加热功能。当时,这则新闻震惊了车圈和所有车主,彼时的大家还没有把汽车当作可以“升级”的智能设备。 如今3年过去了,车主对各家车企的OTA升级早已见…

【halcon算子】set_window_param

算子介绍 set_window_param 是 HALCON 的一个操作符,用于设置已打开窗口的不同参数。以下是一些通用的窗口参数: ‘flush’ 启用或禁用在显示每个对象后刷新窗口内容。如果将 ‘flush’ 设置为 ‘false’,则必须调用 flush_buffer 来更新窗口…

Laravel框架使用phpstudy本地安装的composer用Laravel 安装器进行安装搭建

一、首先需要安装Laravel 安装器 composer global require laravel/installer 二、安装器安装好后,可以使用如下命令创建项目 laravel new sys 三、本地运行 php artisan serve 四、 使用Composer快速安装Laravel5.8框架 安装指定版本的最新版本(推荐&a…