Atomic类如何保证原子性
CAS
全称是CompareAndSwap,它是一条CPU并发原语。用来判断内存某个位置的值是否为预期值,如果是则改为更新的值,这个过程是原子的。AtomicInteger之所以能保证原子性是依赖于UnSafe类,这个类是Java最底层的类之一,里面都是很屌的native方法,都是其他语言写的,咱看不见,Unsafe类可以执行以下几种操作:
- 分配内存,释放内存
- 可以定位对象的属性在内存中的位置,可以修改对象的属性值。使用objectFieldOffset方法
- 挂起和恢复线程,被封装在LockSupport类中供使用
- CAS
相关源码
public final class Unsafe {//源码确实美如画public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;}
}
public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {//这个玩意是内存偏移量valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;/*** Atomically increments by one the current value.** @return the previous value*/public final int getAndIncrement() {//this指的是这个对象本身return unsafe.getAndAddInt(this, valueOffset, 1);}
}
CAS的缺点
- 如果循环时间长,CPU开销很大
- 只能保证一个共享变量的原子操作,对于多个共享变量操作时,循环CAS无法保证操作的原子性,这时候只能使用锁来保证
- 会有ABA问题
ABA
CAS算法是取出内存中某时刻的数据并在当下时刻比较并替换,这中间会有一个时间差导致数据变化。比如说有两个线程,一快一慢,同时从主内存中取变量A,快线程将变量改为B并写入主内存,然后又将B从主内存中取出再改为A写入主内存,这时候慢线程刚完成了工作,使用CAS算法,发现预期值还是A,然后慢线程将自己的结果写入主内存。虽然慢线程操作成功,但是这个过程可能是有问题的
原子引用
AtomicReference用来把特定对象编程原子类,AtomicReference和AtomicInteger非常类似,不同之处就在于AtomicInteger是对整数的封装,底层采用的是compareAndSwapInt实现CAS,比较的是数值是否相等,而AtomicReference则对应普通的对象引用,底层使用的是compareAndSwapObject实现CAS,比较的是两个对象的地址是否相等,也就是它可以保证你在修改对象引用时的线程安全性。
原子引用时间戳
对于ABA问题可以在比较值的同时加上版本号,或者说是时间戳。AtomicStampedReference
这个类在创建的时候要求指定一个初始的版本号,并且每次做CAS操作的时候要求对比版本号,且版本号需要自增
/*** Creates a new {@code AtomicStampedReference} with the given* initial values.** @param initialRef the initial reference* @param initialStamp the initial stamp*/public AtomicStampedReference(V initialRef, int initialStamp) {pair = Pair.of(initialRef, initialStamp);}
/*** Atomically sets the value of both the reference and stamp* to the given update values if the* current reference is {@code ==} to the expected reference* and the current stamp is equal to the expected stamp.** @param expectedReference the expected value of the reference* @param newReference the new value for the reference* @param expectedStamp the expected value of the stamp* @param newStamp the new value for the stamp* @return {@code true} if successful*/public boolean compareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp) {Pair<V> current = pair;returnexpectedReference == current.reference &&expectedStamp == current.stamp &&((newReference == current.reference &&newStamp == current.stamp) ||casPair(current, Pair.of(newReference, newStamp)));}
Atomic类如何保证原子性相关推荐
- setnx是原子操作吗_谈谈Volatile关键字?为什么不能保证原子性?用什么可以替代?为什么?...
大家好,欢迎关注我的公众号码猿bug,需要资料的话可以加我微信好友. 再谈volatile关键字之前,首先必须聊聊JMM内存模型! JMM主要的特性:可见性.原子性,顺序性 Java 虚拟机规范试图定 ...
- volatile不能保证原子性,atomic不仅保证可见性还有原子性CAS分析
给一个变量加了volatile关键字,就会告诉编译器和JVM的内存模型:这个变量是对所有线程共享的.可见的,每次jvm都会读取最新写入的值并使其最新值在所有CPU可见.volatile似乎是有时候可以 ...
- 15.unsafe类的CAS是怎么保证原子性的?
老王:小陈啊,上一章我们讲了usafe是个啥东西,以及unsafe提供的几大类的功能 老王:这一章啊,我们要花个时间专门讲unsafe提供的cas功能,这个cas的功能是我们后面将Atomic原子类体 ...
- 为什么volatile不能保证原子性而Atomic可以?
在上篇<非阻塞同步算法与CAS(Compare and Swap)无锁算法>中讲到在Java中long赋值不是原子操作,因为先写32位,再写后32位,分两步操作,而AtomicLong赋值 ...
- java volatile 原子性_为什么volatile不能保证原子性而Atomic可以?
在上篇<非阻塞同步算法与CAS(Compare and Swap)无锁算法>中讲到在Java中long赋值不是原子操作,因为先写32位,再写后32位,分两步操作,而AtomicLong赋值 ...
- java中的Atomic类
文章目录 问题背景 Lock 使用Atomic java中的Atomic类 问题背景 在多线程环境中,我们最常遇到的问题就是变量的值进行同步.因为变量需要在多线程中进行共享,所以我们必须需要采用一定的 ...
- 18.AtomicReference、AtomicStampReference底层原理。多个变量更新怎么保证原子性?CAS的ABA问题怎么解决?
老王:小陈啊,上一章我们说了AtomicInteger.AtomicBoolean的底层原理,这一篇我们就来说说Atomic系列的另一个分类AtomicReference和AtomicStampRef ...
- 两个原子操作组合到一块不一定是能保证原子性
1.两个原子操作组合到一块不一定是能保证原子性 ConcurrentLinkedQueue AtomicInteger 两个类都为线程安全的类,但是组合起来并不能保证原子性: public stat ...
- volatile学习(可见性,不保证原子性,禁止指令重排(双端检索机制))
volatile是java虚拟机提供的轻量级的同步机制: 1.保证可见性:线程之间可见性(及时通知) 2.不保证原子性 3.禁止指令重排 先了解一下jvm同步 由于JVM运行程序的实体是线程,而每个线 ...
最新文章
- opencv 读取位置 0xFFFFFFFFFFFFFFFF 时发生访问冲突
- 网络层:IP协议详解(IP协议真的得看这篇)
- C/C++ 中判断某一文件或目录是否存在
- 解决ajax重复提交问题?
- PyTorch入门(三)--实现简单图像分类器
- 第一次用PHP做电影站 用thinkphp开发的!!
- ❤️《分布式 Dubbo+Zookenper+SpringBoot》(建议收藏)❤️
- 如何查询硬盘序列号,百度的答案全是错的
- duilib 添加自定义list一例
- php网上花店管理系统的论文,网上花店管理系统
- 计算机电子表格的优点,信息技术《电子表格的特点及应用》的说课稿
- C#字节数组与字符串相互转换代码案例
- java毕业设计房产交易系统Mybatis+系统+数据库+调试部署
- DNS:快速搭建公司内网DNS解析服务器
- python 判断是否是元音字母
- 吴军老师《给中学生/大学生的书单》----Yohao整理
- spring-boot 2.5.6 问题:WARN: This connection is using TLSv1.1
- Java教程,Java视频,培训机构免费资源下载汇总帖,全网最有情怀,集合所以品牌机构(持续更新...)
- C++在线编辑器:cpp.sh
- vue 前端生成二维码,并转换为图片
热门文章
- Mac下嵌入式开发初步(二)
- dw常用标签_dreamweaver中常用到的标签及含义
- SQL Server健康检查的重要性
- win10去掉快捷方式小箭头_电脑一分钟小技巧:桌面快捷方式小箭头去除与恢复方法...
- 计算机视觉(视频追踪检测分类、监控追踪)常用测试数据集
- IDEA小技巧之痛苦面具 主菜单不见了怎么办?
- Flask_从入门到放弃?不!!!从入门到入土!!
- Swift之函数的语法和使用 | CSDN创作打卡
- 【Linux】Linux 开放端口
- c语言作业订单号查询,C语言 查询订单系统进不去 还请高手指点