Atomic原子类的使用及其原理

原子类 AtomicReference 详解 通过对 AtomicInteger、AtomicBoolean 和 AtomicLong 分析我们发现,这三个原子类只能对单个变量进行原子操作,那么我们如果要对多个变量进行原子操作,这三个类就无法实现了。那如果要进行多个变量进行原子操作呢?操作方式就是,先把 多个变量封装成一个类,然后通过 AtomicReference 进行操作。 阅读详情

概述

Atomic,在化学中原子指的是不可分割的实体。同样的在并发体系中,原子类则是所有操作都具有原子性的,也就是说它的一个操作一旦开始,就不会被其他线程干扰。同时原子类是"并java并发体系"中,无锁方案的重要组成部分。

在之前的文章中–“并发知识梳理”,这篇文章中我们提出了累加器问题:

当多个线程同时访问下边这个累加方法时,会出现最终结果小于实际累加值的情况,并且每次执行的时候最终结果都是不确定的。

public class UnsafeSequence {
	private int value = 0;
	public int getNext() {
		return ++value;
	}
}

当时分析后,我们知道造成这种不确定性的原因在于我们这个累加器的加法操作++value不具有原子性导致的,如果我们的加法操作使用原子操作则可以避免这个问题。

使用

在下面的代码中,我们将原来的 long 型变量 count 替换为了原子类 AtomicLong,原来的 count +=1 替换成了 count.getAndIncrement(),仅需要这两处简单的改动就能使 add10K() 方法变成线程安全的,原子类的使用还是挺简单的。

public class SafeSequence{
  AtomicLong count = new AtomicLong(0);
  public long getNext() {
     return count.getAndIncrement();
  }
}

❓ 好像很神奇,但它内部是如何保证getAndIncrement()是原子操作的呢?

CAS原理

其实其内部原理非常简单,都是通过CAS(Compare And Sweep,比较并交换)指令来实现。

CAS 的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。可能说起来比较抽象,我们结合getAndIncrement()源码来看:

public final long getAndIncrement() {
    return U.getAndAddLong(this, VALUE, 1L);
}

U.getAndAddLong()方法的源码如下,该方法首先会在内存中读取共享变量的值,之后循环调用compareAndSwapLong()方法来尝试设置共享变量的值,直到成功为止。compareAndSwapLong() 是一个 native 方法,只有当内存中共享变量的值等于 expected 时,才会将共享变量的值更新为 x,并且返回 true;否则返回 fasle。compareAndSwapLong 的语义和 CAS 指令的语义的差别仅仅是返回值不同而已。

public final long getAndAddLong(Object o, long offset, long delta) {
    long v;
    do {
        v = getLongVolatile(o, offset);
    } while (!weakCompareAndSetLong(o, offset, v, v + delta));
    return v;
}

//当从内存中读取的o.offset的值和expected值相同时,将o.offset的值更新为x
native boolean compareAndSwapLong(  Object o, long offset, long expected,  long x);

使用 CAS 来解决并发问题,一般都会伴随着自旋,而所谓自旋,其实就是循环尝试。上边的代码也不例外,

do {
	读取旧变量的值
} while (compareAndSet(expect, newV);

在自旋过程中,如果CAS设置失败,会重新读取变量的值,再次进行CAS操作,直到成功。

通过CAS+自旋可以实现以无锁的方式更新变量的值,但可能带了一个ABA问题,什么是ABA问题呢?

简单来说,第一个线程取到了变量 x 的值 A,然后巴拉巴拉干别的事,总之就是只拿到了变量 x 的值 A。这段时间内第二个线程也取到了变量 x 的值 A,然后把变量 x 的值改为 B,然后巴拉巴拉干别的事,最后又把变量 x 的值变为 A (相当于还原了)。在这之后第一个线程终于进行了变量 x 的操作,但是此时变量 x 的值还是 A,所以 compareAndSet 操作是成功。

可能大多数情况下我们并不关心 ABA 问题,例如数值的原子递增,但也不能所有情况下都不关心,例如原子化的更新对象很可能就需要关心 ABA 问题,因为两个 A 虽然相等,但是第二个 A 的属性可能已经发生变化了。所以在使用 CAS 方案的时候,一定要先 check 一下。

Java中常用的原子类

image.png

基本类型原子类

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

上面三个类提供的方法几乎相同,所以我们这里以 AtomicInteger 为例子来介绍。

public final int get() //获取当前的值
public final int getAndSet(int newValue)//获取当前的值,并设置新的值
public final int getAndIncrement()//获取当前的值,并自增
public final int getAndDecrement() //获取当前的值,并自减
public final int getAndAdd(int delta) //获取当前的值,并加上预期的值
boolean compareAndSet(int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
public final void lazySet(int newValue)//最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

数组类型原子类

  • AtomicIntegerArray:整形数组原子类
  • AtomicLongArray:长整形数组原子类
  • AtomicReferenceArray :引用类型数组原子类

这些类提供的方法和原子化的基本数据类型的区别仅仅是:每个方法多了一个数组的索引参数,所以这里也不再赘述了。其常用方法如下:

public final int get(int i) //获取 index=i 位置元素的值
public final int getAndSet(int i, int newValue)//返回 index=i 位置的当前的值,并将其设置为新值:newValue
public final int getAndIncrement(int i)//获取 index=i 位置元素的值,并让该位置的元素自增
public final int getAndDecrement(int i) //获取 index=i 位置元素的值,并让该位置的元素自减
public final int getAndAdd(int i, int delta) //获取 index=i 位置元素的值,并加上预期的值
boolean compareAndSet(int i, int expect, int update) //如果输入的数值等于预期值,则以原子方式将 index=i 位置的元素值设置为输入值(update)
public final void lazySet(int i, int newValue)//最终 将index=i 位置的元素设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。

引用类型原子类

  • AtomicReference:引用类型原子类
  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
  • AtomicMarkableReference :原子更新带有标记的引用类型。该类将 boolean 标记与引用关联起来,本质就是它的版本号只有两个,true和false,这个类只能减少ABA问题的发生,但不能从根本上解决ABA问题。

前面引用类型原子类可能出现ABA问题,如何解决该问题呢?

其实也很简单,只要我们在更新版本的时候增加一个版本号就可以解决,基于此设计了AtomicStampedReference

也就是说在使用boolean compareAndSet( V expectedReference, V newReference, int expectedStamp)增加了一个版本字段字段newStam变成了boolean compareAndSet( V expectedReference, V newReference, int expectedStamp,int newStam)。

总结

本文总结了常用的原子类,及其内部实现的原理–CAS原理。Java 提供的原子类能够解决一些简单的原子性问题,但你可能会发现,上面我们所有原子类的方法都是针对一个共享变量的,如果你需要解决多个变量的原子性问题,建议还是使用互斥锁方案。原子类虽好,但使用要慎之又慎。

引用

  1. 《 原子类:无锁工具类的典范》
  2. 《Atomic原子类总结》
四、原子操作之 AtomicReference 引用就是为对象另起一个名字,引用对象本身指向被引用对象,对引用对象的操作都会反映到被引用对象上。包中还提供了更多更细场景的原子操作类,此包下的大部分工具类都是基于 CAS 原理实现,正因为如此,有很多相似之处,用法大同小异,希望大家在日常研发中多比较多总结,早日掌握之。工具类实现生活中汽车牌照竞拍的例子:假设总共有 10 位客户参与竞拍,每位客户只有一次竞拍机会,竞拍是资格竞拍不以竞拍价格为目的。则是对普通的对象引用的封装,可将多个变量作为一个整体对象,操控多个属性的原子性的并发类。 阅读详情

相关推荐

Java之美[从菜鸟到高手演练]之atomic包的原理及分析

Atomic包是java.util.concurrent下的另一个专门为线程安全设计的Java包,包含多个原子操作类。这个包里面提供了一组原子变量类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。实际上是借助硬件的相关指令来实现的,不会阻塞线程(或者说只是在硬件级别上阻塞了)。

智慧演绎,无处不在 8万+

原子类型:AtomicReference详解

文章目录1 AtomicReferencey引入2 AtomicReference的基本用法2.1 AtomicReference的构造2.2 方法 1 AtomicReferencey引入 AtomicReference类提供了对象引用的非阻塞原子性读写操作,并且提供了其他一些高级的用法 对象的引用其实是一个4字节的数字,代表着在JVM堆内存中的引用地址,对一个4字节数字的读取操作和写入操作本身就是原子性的**,通常情况下,我们对对象引用的操作一般都是获取该引用或者重新赋值(写入操作),我们也没有办法对对

wyaoyao93的博客 4万+

「死磕Java并发编程」说说Java Atomic 原子类的实现原理

<!-- TOC --> - [线程安全真的是线程的安全吗?](#线程安全真的是线程的安全吗) - [什么是 Atomic?](#什么是-atomic) - [实现一个计数器](#实现一个计数器) - [AtomicInteger 源码分析](#atomicinteger-源码分析) - [AtomicLong 和 LongAdder 谁更牛?](#atomiclong-和-longadder-谁更牛) - [总结](#总结) <!-- /TOC --> ..

华科硕士,大厂打工中。编程问题咨询,请私我 1万+

AtomicReference详解

在开发中突然看到这个类,就学习了以下AtomicReference,希望可以帮到你

不定期更新Java相关的各种内容 1万+

详细解答什么是原子类,原子类的常用类有哪些,Atomic的原理是怎么样的

原子类是Java中提供的一种用于并发编程的工具类,它们位于java.util.concurrent.atomic包中。这些类通过底层硬件的原子操作来实现无锁的线程安全编程。原子类的主要优势在于,它们提供了一种避免使用同步机制(如synchronized关键字)而实现线程安全的方法,从而在高并发场景下提高性能。

小小鱼儿小小林的博客 266

DIY必备:如何用N-MOS管搭建低成本DC有刷电机驱动电路(附原理图+避坑指南)

本文详细介绍了如何使用N-MOS管搭建低成本、高可靠性的DC有刷电机驱动电路。针对DIY爱好者,从核心原理、电路设计、元件选型到焊接调试,提供了完整的实战指南,并重点解析了续流二极管选型、栅极驱动设计等关键避坑要点,帮助读者成功驱动电机并确保电路稳定运行。

delta的博客 775

Atomic原子类及原理

一、引入 原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作是原子操作。再比如:a++;这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。 但是,像i++这种非原子操...

奋进的小白粥 5698

那些年对支付宝的研究[整理]

支付宝功能强大.他内部各个功能间的跳转是通过Uri 实现的.而使用window.location.href 即可.跟浏览器一样的协议.很方便 H5启动支付宝: alipays://platformapi/startapp?appId=20000067&url=网址 alipays://platformapi/startapp?appId=20000691&url=网址 ali...

技术专研 8915

Atomic原子类体系及CAS原理详解

java 1.5引进原子类,具体在java.util.concurrent.atomic包下,atomic包里面一共提供了13个类,分为4种类型,分别是:原子更新基本类型,原子更新数组,原子更新引用,原子更新属性。原子类也是java实现同步的一套解决方案。基本类型:AtomicBoolean:布尔型AtomicInteger:整型AtomicLong:长整型数组:AtomicIntegerArray:数组里的整型AtomicLongArray:数组里的长整型。...

GeGe&YoYo的博客 3282

手把手教你设计T型和Π型衰减器:从理论计算到实际搭建(含误差分析)

本文详细介绍了T型和Π型衰减器的设计方法,从理论计算到实际搭建,包括电阻值推导、元件选择技巧及误差分析。通过实例演示如何设计50Ω系统中的10dB衰减器,并提供了高频应用和优化策略,帮助电子工程师掌握阻抗匹配与信号衰减的核心技术。

weixin_33737774的博客 395

Android 13高通平台CTS camera部分fail项修改(续)

接着上一篇,有几个提了case高通也分析了很久的fail项,其中最离谱的是问题1,第一个接手case的人反反复复处理、内部排查踢皮球、要求log,折腾了很久,最终就给出个解不了的结论。后面还是靠着自己发现了一些蛛丝马迹,并且换了个case接手人才最终解决。

hkj887tg的博客 2060

淘宝Java面试被问:Atomic原子类的实现原理

摘要:Atomic原子类基于CAS(Compare-And-Swap)实现无锁并发,通过CPU级原子指令和volatile保证可见性。核心机制包括:1)Unsafe类提供硬件级CAS操作;2)原子类家族(基础类、数组类、引用类等)满足不同场景;3)AtomicStampedReference解决ABA问题。JDK8+增强包括LongAdder分段累加和函数式更新。使用时需注意:高竞争场景选LongAdder,复杂操作仍需锁,避免伪共享和过度创建对象。Atomic结合CAS+volatile+Unsafe成为

x2277659985的博客 1480

微信小程序开发:实现地图导航功能

其中,id用于调用地图组件,latitude和longitude表示地图的中心点坐标,markers表示地图中的标记点,covers表示地图中的覆盖物,polyline表示地图中的折线图,show-location表示是否显示当前定位点,bindregionchange表示移动地图时触发的事件。其中,id表示标记点的唯一标识,latitude和longitude表示标记点的坐标,title表示标记点的名称,iconPath表示标记点的图标路径,width和height表示标记点的宽度和高度。

吃不胖. 8920

JDK 中 Atomic 开头的类实现原子性的原理是什么?

JDK 中 Atomic 开头的类实现原子性的原理是什么? JDK Atomic开头的类,是通过 CAS 原理解决并发情况下原子性问题。 CAS 包含 3 个参数,CAS(V, E, N)。V 表示需要更新的变量,E 表示变量当前期望值,N 表示更新为的值。只有当变量 V 的值等于 E 时,变量 V 的值才会被更新为 N。如果变量 V 的值不等于 E ,说明变量 V 的值已经被更新...

ConstXiong 5701

OpenClaw入门必装Skills(附使用场景)

很多同学安装了OpenClaw后,但却不会使用,就相当于你只是拥有了一副强大的躯干,却没有给它装上能够灵活执行任务的“手脚”和负责思考决策的“大脑插件”——Skills。没有这些核心能力的支撑,OpenClaw只是一个“空壳”,无法真正应用到实际业务场景中,更谈不上提升效率或解决复杂问题。所以,安装只是第一步,要真正让OpenClaw“活”起来,为我们所用,就需要给OpenClaw安装各种Skills,这个过程就是养虾的过程。

十二测试录 3224

lammps蒙特卡洛MC模拟案例

在lammps中,和MC相关的模拟命令有三个:fix gcmc、fix atom/swap、fix sgcmc。在一个lammps模拟中,mc和md可同时存在,间隔运行,所以设置fix sgcmc命令的同时,可以同时设置系综(nve、nvt或者npt)。lammps官网自带案例中也有一个fix sgcmc的案例,主要模拟FeCu合金的MC建模。sgcmc命令可应用到合金的模拟中,如短程有序模拟。MC运行间隔为100步,最终的浓度比例为0.02,也就是2%的Fe原子交换为Cu原子。大家好,我是小马老师。

lammps_jiayou的博客 1825

Atomic原子类及底层原理

Atomic简介 在多线程同时操作一个变量的时候,类似++这种方式,用锁就显得大材小用了,所以用到了atomic原子类,可以保证在多线程的情况下,安全高性能的执行程序更新变量,atomic原子类的底层不是传统意义上的锁机制,而是无锁化的cas机制,CAS 全称compare and set 比较后再设置 简单理解就是多线程操作的情况下先获取一个值,看谁先发起cas操作,判断时候和获取的时候值一样 如果一样就修改值,如果不一样就重新获取再进行修改 jdk1.8之前内就是通过cas机制不断地循环判断,这样在并

qq_27081015的博客 1611

CloudCompare 多窗口联动显示点云

CloudCompare支持多窗口联动显示多个点云数据,可以用来比对原始点云数据与语义分割后的结果。 一、创建多个视窗 打开CloudCompare后会默认有一个视图,我们要同时打开两个数据就需要再创建一个视图,选择菜单栏里的3D Views,在下拉菜单中点击new,这样就创建了一个新的视图,这时3D Views的下拉菜单中有3D View 1和3D View 2两个视图,然后点击3D Views下拉菜单中的Title,这时在数据展示区就会出现两个并列的窗口。 二、分别加载点云数据 将数据分别拖进两个

zhaoguanhua的博客 2740

atomic原子类实现机制_atomic实现原理

atomic是原子的意思,意味"不可分割"的整体。在Linux kernel中有一类atomic操作API。这些操作对用户而言是原子执行的,在一个CPU上执行过程中,不会被其他CPU打断。最常见的操作是原子读改写,简称RMW。例如,atomic_inc()接口。atomic硬件实现和Cache到底有什么关系呢?其实有一点关系,下面会一步步揭晓答案。问题背景我们先来看看不使用原子操作的时候,我们会遇...

weixin_30241053的博客 1097

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

JUC多线程:Atomic原子类与CAS原理

tomic 原子操作类是基于无锁 CAS + volatile 实现的,并且类中的所有方法都使用 final 修饰,进一步保证线程安全。而 CAS 算法的具体实现方式在于 Unsafe 类中,Unsafe 类的所有方法都是 native 修饰的,也就是说所有方法都是直接调用操作系统底层资源进行执行相应任务。Atomic 使用乐观策略,每次操作时都假设没有冲突发生,并采用 volatile 配合 CAS 去修改内存中的变量,如果失败则重试,直到成功为止。

张维鹏的博客 9965

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Atomic 原子类介绍及其原理

简单介绍一下CAS(compareAndSwap),是一种无锁原子算法,映射到操作系统就是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值,其实现方式是基于硬件平台的汇编指令,在intel的CPU中,使用的是cmpxchg指令,就是说CAS是靠硬件实现的,从而在硬件层面提升效率。仅当 V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程完成更新,则当前线程则什么都不做,最后CAS 返回当前V的真实值。

goTsHgo的博客 681

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

JUC中Atomic包6种原子类使用详解及原理源码分析,看这一篇文章就够了

6类原子类 原子类类型 JUC的atomic归类 Atomic*基本原子类 AtomicIntegerAtomicLongAtomicBoolean Atomic*Array数组类型原子类 AtomicIntegerArrayAtomicLongArrayAtomicReferenceArray AtomicReference引用类型原子类 AtomicReferenceAtomicStampedReference AtomicMarkableReference AtomicFi

weixin_41590779的博客 846

Atomic原子类的实现原理

线程安全真的是线程的安全吗?什么是 Atomic?实现一个计数器AtomicInteger 源码分析AtomicLong 和 LongAdder 谁更牛?总结当我们谈论『线程安全』的时候,...

weixin_45727359的博客 305

宇树科技Java面试被问:Atomic原子类的实现原理(CAS机制)

本文深度解析了CAS(Compare-And-Swap)机制在Java原子类中的实现原理。主要内容包括:1)CAS的核心思想是通过乐观锁和无锁方式实现原子操作;2)详细剖析AtomicInteger等原子类的源码实现,包括内存偏移量计算和硬件指令支持;3)探讨CAS的三大问题(ABA问题、自旋开销、多变量原子性)及解决方案;4)提出多种性能优化策略,如缓存行填充、批量CAS和自适应自旋;5)介绍现代JVM对CAS的增强特性,包括VarHandle和架构优化;6)总结生产环境中的最佳实践,包括监控诊断和配置优

2501_94505843的博客 1486

Atomic原子类及其底层原理

并发包java.util.concurrent的原子类都存放在java.util.concurrent.atomic下 JUC 包中的原子类 基本类型 AtomicInteger:整形原子类 AtomicLong:长整型原子类 AtomicBoolean:布尔型原子类 AtomicInteger 类常用方法 public final int get() //获取当前的值 public final int getAndSet(int newValue)//获取当前的值,并设置新的值 publi

weixin_39802680的博客 1110

【并发编程系列11】Java中12个原子(Atomic)操作类实现原理分析

boolean getAndSet(boolean newValue): 以原子方式将值设置为newValue,并返回旧值。 AtomicLong 这个和上面的AtomicInteger几乎是一样的,就不在举例了。 原子操作都是利用Unsafe类中的CAS操作实现的,但是Unsafe中只提供了三种类型的CAS操作: 所以上面的boolean类型是转换为整型来CAS的,其他数据类型也可以先进行数据转换之后再通过CAS实现原子操作。 原子更新数组 ===========================.

澎湖Java架构师的博客 495
上一篇: Kafka的核心原理
下一篇: 线程本地存储ThreadLocal的原理及应用
vcjmhg
博客等级 码龄11年 27粉丝 94原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值