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.mzph.cn/news/239603.shtml

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

相关文章

WebAssembly 的魅力:高效、安全、跨平台(下)

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…

Unity中的导航系统

NavMesh(导航网格)是3D游戏世界中用于实现动态物体自动寻路的一种技术,将游戏中复杂的结构组织关系简化为带有一定信息的网格,在这些网格的基础上通过一系列的计算来实现自动寻路。导航时,只需要给导航物体挂载导航组建,导航物体便…

基于Rocket MQ扩展的无限延迟消息队列

基于Rocket MQ扩展的无限延迟消息队列 背景: Rocket MQ支持的延迟队列时间是固定间隔的, 默认19个等级(包含0等级): 0s, 1s, 5s, 10s, 30s, 1m, 2m, 3m, 4m, 5m, 6m, 7m, 8m, 9m, 10m, 20m, 30m, 1h. 我们的需求是实现用户下单后48小时或72小时给用户发送逼单邮件. 使用默认的…

判断平衡二叉树与翻转二叉树——每日练习

目录 1、是否是平衡二叉树 初阶实现 分析 时空复杂度 进阶实现 分析 时空复杂度 总结 2、翻转二叉树 分析 时空复杂度 1、是否是平衡二叉树 110. 平衡二叉树 - 力扣(LeetCode) 初阶实现 /*** Definition for a binary tree node.* stru…

Linux 下更新 Host

文章目录 更新Host Linux 下更新 Host Linux下下载github代码,经常超时,有时候可通过更新host来解决。以下梳理总结几种更新 host的方法。 更新Host #方法一(openwrt,也利用于linux) wget -q https://gitlab.com/ineo6/hosts/-/raw/master/…

【Grafana】Grafana匿名访问以及与LDAP连接

上一篇文章利用Docker快速部署了Grafana用来展示Zabbix得监控数据,但还需要给用户去创建账号允许他们登录后才能看展示得数据,那有什么办法让非管理员更方便得去访问Grafana呢?下面介绍两个比较方便实现的: 在开始设置前&#xff…

显示器屏幕oled的性能、使用场景、维护

OLED显示器屏幕具有许多独特的性能和使用场景,以下是关于OLED显示器屏幕的性能、使用场景和维护的详细介绍: 一、性能 色彩鲜艳:OLED显示器屏幕能够呈现出更加鲜艳的色彩,色彩饱和度高,色彩还原性好,可以给…

SpringBoot+JaywayJsonPath实现Json数据的DSL(按照指定节点表达式解析json获取指定数据)

场景 若依前后端分离版手把手教你本地搭建环境并运行项目: 若依前后端分离版手把手教你本地搭建环境并运行项目_前后端分离项目本地运行-CSDN博客 在上面搭建SpringBoot项目的基础上,并且在项目中引入fastjson、hutool等所需依赖后。 Jayway JsonPat…

【【C++11特性篇】【强制/禁止 】生成默认函数的关键字default&delete(代码演示)

前言 大家好吖,欢迎来到 YY 滴C系列 ,热烈欢迎! 本章主要内容面向接触过C的老铁 主要内容含: 欢迎订阅 YY滴C专栏!更多干货持续更新!以下是传送门! YY的《C》专栏YY的《C11》专栏YY的《Linux》…

动物分类识别教程+分类释义+界面展示

1.项目简介 动物分类教程分类释义界面展示 动物分类是生物学中的一个基础知识,它是对动物进行分类、命名和描述的科学方法。本教程将向您介绍动物分类的基本原则和方法,并提供一些常见的动物分类释义。 动物分类的基本原则 动物分类根据动物的形态、…

vue3老项目如何引入vite

vue3老项目如何引入vite 安装 npm install vite vitejs/plugin-vue --save-dev Vite官方中文文档修改package.json文件 在 npm scripts 中使用 vite 执行文件 "scripts": {"serve": "vite","build": "vite build","pr…

听GPT 讲Rust源代码--src/tools(22)

File: rust/src/tools/tidy/src/lib.rs rust/src/tools/tidy/src/lib.rs是Rust编译器源代码中tidy工具的实现文件之一。tidy工具是Rust项目中的一项静态检查工具,用于确保代码质量和一致性。 tidy工具主要有以下几个作用: 格式化代码:tidy工具…

Rust报错:the msvc targets depend on the msvc linker but `link.exe` was not found

当我在我的 windows 电脑上安装 rust,然后用 cargo 新建了一个项目后,cargo run 会报错: error: linker link.exe not found| note: program not foundnote: the msvc targets depend on the msvc linker but link.exe was not foundnote: p…

css学习笔记6(盒子模型)

CSS盒子模型 五、CSS盒子模型1.CSS长度单位2.元素的显示模式3.总结各元素的显示模式4.修改元素显示模式5.盒子模型的组成6.盒子内容区(content)7.关于默认宽度8.盒子内边距(padding)9.盒子边框(border)10.盒…

Apache Flink 进阶教程(七):网络流控及反压剖析

目录 前言 网络流控的概念与背景 为什么需要网络流控 网络流控的实现:静态限速 网络流控的实现:动态反馈/自动反压 案例一:Storm 反压实现 案例二:Spark Streaming 反压实现 疑问:为什么 Flink(bef…

4-Docker命令之docker load

1.docker load介绍 docker load命令是用来将从tar归档文件载入镜像 docker load命令相对应的命令是docker save 2.docker load用法 docker load [参数] [root@centos79 ~]# docker load --helpUsage: docker load [OPTIONS]Load an image from a tar archive or STDINAli…

基于Netty构建Websocket服务端

除了构建TCP和UDP服务器和客户端,Netty还可以用于构建WebSocket服务器。WebSocket是一种基于TCP协议的双向通信协议,可以在Web浏览器和Web服务器之间建立实时通信通道。下面是一个简单的示例,演示如何使用Netty构建一个WebSocket服务器。 项目…

深圳鼎信|输电线路防山火视频监控预警装置:森林火灾来袭,安全不留白!

受线路走廊制约和环保要求影响,输电线路大多建立在高山上,不仅可以减少地面障碍物和人类活动的干扰,还能提高线路的抗灾能力和可靠性。但同时也会面临其它的难题,例如森林火灾预防。今天,深圳鼎信智慧将从不同角度分析…

在电路实际设计中PCI 的三态和 OD、OC 信号要有上拉。

在PCI总线以及其他数字电路设计中,三态(Tri-state)、开漏(Open Drain, OD)和开集(Open Collector, OC)是常见的输出信号类型。这些信号类型通常需要外部上拉电阻来确保信号线在不被任何设备驱动时能够被拉到高电平状态。 三态(Tri-state)信号 ,三态信号具有高电平、低…

signaltap立即触发的错误解决方法

signaltap点下run analysis后没有等到触发条件满足就触发了,原因是触发方式设置错误,应修改触发方式: 将Trigger flow control 从State-based 改为Sequential。