一、Atomic 原子类的原理:

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

  • 乐观锁:乐观锁认为竞争不总是发生,因此每次操作共享资源时都不需要加锁,并将“比较-替换”这两个动作作为一个原子操作去修改内存中的变量,一旦发生冲突就重试,直到成功为止。无锁策略就是一种乐观策略,采用 volatile + CAS 来保证线程执行的安全性。

  • 悲观锁:悲观锁认为每次访问共享资源总会发生冲突,因此每次对共享资源进行操作时,都会去事先申请一个独占的锁,比如 synchronized 和 ReentronLock 都是独占锁。

二、什么是 CAS:Compare And Swap:

1、CAS 的算法核心思想:

执行函数:CAS(V,E,U),其包含3个参数:内存值V,旧预期值E,要修改的值U。

  • ① 当且仅当 预期值E 和 内存值V 相同时,才会将内存值修改为U并返回true;

  • ② 若V值和E值不同,则说明已经有其他线程做了更新,则当前线程不执行更新操作,但可以选择重新读取该变量再尝试再次修改该变量,也可以放弃操作。

CAS 一定要 volatile 变量配合,这样才能保证每次拿到的变量是主内存中最新的那个值,否则旧的预期值E对某条线程来说,永远是一个不会变的值E,只要某次CAS操作失败,永远都不可能成功。由于 CAS 无锁操作中没有锁的存在,因此不可能出现死锁的情况,也就是天生免疫死锁。

2、CPU 指令对 CAS 的支持:

由于 CAS 的步骤很多,那会不会存在一种情况:假设某个线程在判断 V 和 E 相同后,正要赋值时,切换了线程,更改了值,从而造成了数据不一致呢?答案是否定的,因为 CAS 是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。

3、CAS 的 ABA 问题及其解决方案:

假设这样一种场景,当第一个线程执行 CAS(V,E,U) 操作,在获取到当前变量V,准备修改为新值U前,另外两个线程已连续修改了两次变量V的值,使得该值又恢复为旧值,这样的话,我们就无法正确判断这个变量是否已被修改过。

解决方法:使用带版本的标志或者时间戳解决ABA问题,在更新数据时,只有要更新的数据和版本标识符合期望值,才允许替换。

三、Unsafe 类:

Atomic 中 CAS 操作的执行依赖于 Unsafe 类的方法,Unsafe 类中的所有方法都是 native 修饰的,也就是说所有方法都直接调用操作系统底层资源执行相应任务。Unsafe类提供了很多功能,这里我们主要介绍 Unsafe 的 CAS,对其他功能感兴趣的读者可以去阅读这篇文章:https://blog.csdn.net/javazejian/article/details/72772470

Unsafe 类存在于 sun.misc 包中,其内部方法操作可以像C的指针一样直接操作内存,单从名称看来就可以知道该类是非安全的,因为 Unsafe 拥有着类似于C的指针操作,因此总是不应该首先使用 Unsafe 类,Java 官方也不建议直接使用的 Unsafe 类

无锁操作 CAS 是一些CPU直接支持的指令,在 Java 中无锁操作 CAS 基于以下3个方法实现,在稍后讲解Atomic系列内部方法就是基于下述方法的实现的。

//第一个参数o为给定对象,offset为对象内存的偏移量,通过这个偏移量迅速定位字段并设置或获取该字段的值,
//expected表示期望值,x表示要设置的值,下面3个方法都通过CAS原子指令执行操作。
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 类中在 JDK8 新增的几个方法,它们的实现是基于上述的CAS方法,如下:

 //1.8新增,给定对象o,根据获取内存偏移量指向的字段,将其增加delta,//这是一个CAS操作过程,直到设置成功方能退出循环,返回旧值public final int getAndAddInt(Object o, long offset, int delta) {int v;do {//获取内存中最新值v = getIntVolatile(o, offset);//通过CAS操作} while (!compareAndSwapInt(o, offset, v, v + delta));return v;}//1.8新增,方法作用同上,只不过这里操作的long类型数据public final long getAndAddLong(Object o, long offset, long delta) {long v;do {v = getLongVolatile(o, offset);} while (!compareAndSwapLong(o, offset, v, v + delta));return v;}//1.8新增,给定对象o,根据获取内存偏移量对于字段,将其 设置为新值newValue,//这是一个CAS操作过程,直到设置成功方能退出循环,返回旧值public final int getAndSetInt(Object o, long offset, int newValue) {int v;do {v = getIntVolatile(o, offset);} while (!compareAndSwapInt(o, offset, v, newValue));return v;}// 1.8新增,同上,操作的是long类型public final long getAndSetLong(Object o, long offset, long newValue) {long v;do {v = getLongVolatile(o, offset);} while (!compareAndSwapLong(o, offset, v, newValue));return v;}//1.8新增,同上,操作的是引用类型数据public final Object getAndSetObject(Object o, long offset, Object newValue) {Object v;do {v = getObjectVolatile(o, offset);} while (!compareAndSwapObject(o, offset, v, newValue));return v;}

四、原子操作类 Atomic:

原子更新基本类型主要包括3个类:

  • AtomicBoolean:原子更新布尔类型

  • AtomicInteger:原子更新整型

  • AtomicLong:原子更新长整型

这3个类的实现原理和使用方式几乎是一样的,这里我们以 AtomicInteger 为例进行分析,AtomicInteger 主要是针对 int 类型的数据执行原子操作,它提供了原子自增方法、原子自减方法以及原子赋值方法等,鉴于AtomicInteger的源码不多,我们直接看源码:

public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// 获取指针类Unsafeprivate static final Unsafe unsafe = Unsafe.getUnsafe();//下述变量value在AtomicInteger实例对象内的内存偏移量private static final long valueOffset;static {try {//通过unsafe类的objectFieldOffset()方法,获取value变量在对象内存中的偏移//通过该偏移量valueOffset,unsafe类的内部方法可以获取到变量value对其进行取值或赋值操作valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}//当前AtomicInteger封装的int变量valueprivate volatile int value;public AtomicInteger(int initialValue) {value = initialValue;}public AtomicInteger() {}//获取当前最新值,public final int get() {return value;}//设置当前值,具备volatile效果,方法用final修饰是为了更进一步的保证线程安全。public final void set(int newValue) {value = newValue;}//最终会设置成newValue,使用该方法后可能导致其他线程在之后的一小段时间内可以获取到旧值,有点类似于延迟加载public final void lazySet(int newValue) {unsafe.putOrderedInt(this, valueOffset, newValue);}//设置新值并获取旧值,底层调用的是CAS操作即unsafe.compareAndSwapInt()方法public final int getAndSet(int newValue) {return unsafe.getAndSetInt(this, valueOffset, newValue);}//如果当前值为expect,则设置为update(当前值指的是value变量)public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}//当前值加1返回旧值,底层CAS操作public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}//当前值减1,返回旧值,底层CAS操作public final int getAndDecrement() {return unsafe.getAndAddInt(this, valueOffset, -1);}//当前值增加delta,返回旧值,底层CAS操作public final int getAndAdd(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta);}//当前值加1,返回新值,底层CAS操作public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;}//当前值减1,返回新值,底层CAS操作public final int decrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, -1) - 1;}//当前值增加delta,返回新值,底层CAS操作public final int addAndGet(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta) + delta;}//省略一些不常用的方法....
}

可以发现 AtomicInteger 原子类的内部几乎是基于 Unsafe 类中的 CAS 相关操作的方法实现的,这也同时证明 AtomicInteger 是基于无锁实现的,这里重点分析自增操作实现过程,其他方法自增实现原理一样。

//当前值加1,返回新值,底层CAS操作
public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;}

我们发现 AtomicInteger 类中所有自增或自减的方法都间接调用 Unsafe 类中的 getAndAddInt() 方法实现了CAS操作,从而保证了线程安全,关于 getAndAddInt() 其实前面已分析过,它是 Unsafe 类中1.8新增的方法,源码如下:

//Unsafe类中的getAndAddInt方法
public final int getAndAddInt(Object o, long offset, int delta) {int v;do {v = getIntVolatile(o, offset);} while (!compareAndSwapInt(o, offset, v, v + delta));return v;}

可看出 getAndAddInt() 通过一个 while 循环不断的重试更新要设置的值,直到成功为止,调用的是 Unsafe 类中的 compareAndSwapInt() 方法,是一个 CAS 操作方法。这里需要注意的是,上述源码分析是基于JDK1.8的,如果是1.8之前的方法,AtomicInteger 源码实现有所不同,是基于 for 死循环的,如下:

//JDK 1.7的源码,由for的死循环实现,并且直接在AtomicInteger实现该方法,JDK1.8后,该方法实现已移动到Unsafe类中,直接调用getAndAddInt方法即可
public final int incrementAndGet() {for (;;) {int current = get();int next = current + 1;if (compareAndSet(current, next))return next;}
}

JUC多线程:Atomic原子类与CAS原理相关推荐

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

    <死磕 Java 并发编程>系列连载中,大家可以关注一波. 「死磕 Java 并发编程」阿里二面,面试官:说说 Java CAS 原理? 「死磕 Java 并发编程」面试官:说说什么是 J ...

  2. 关于JCU并发包中的Atomic原子类及其CAS

    希望造成叫醒你的是梦想 而不是闹钟 目录 类型 常用方法 CAS CAS与Atomic原子类 自旋锁 CAS缺点 Atomic原子类: 在化学中,原子是构成一般物质的最小单位,是不可分割的.而在这里, ...

  3. atomic原子类实现机制_JUC学习笔记--Atomic原子类

    Atomic 原子操作类包 Atomic包 主要是在多线程环境下,无锁的进行原子操作.核心操作是基于UnSafe类实现的CAS方法 CAS CAS: compareAndSwap :传入两个值:期望值 ...

  4. Java多线程进阶面试-Atomic 原子类

    1.介绍一下 Atomic 原子类 Atomic 翻译成中文是原子的意思.在化学上,我们知道原子是构成一般物质的最小单位,在化学反应中是不可分割的.在我们这里 Atomic 是指一个操作是不可中断的. ...

  5. 原子性概念及原子类的工作原理-CAS机制

    1.原子性概念 原子性是指一个操作是不可中断的,要么全部执行成功,要么全部执行失败,有着"同生共死"的感觉.即使在多个线程一起执行的时候,一个操作一旦开始,就不会被其它的线程干扰. ...

  6. Atomic原子类及原理

    目录 1 前言 2 unsafe类对Atomic原子类的支持 3 AtomicInteger的内部实现 3.1 准备 3.2 读 3.3 写 4 CAS机制 4.1 基本操作数 4.2 例子 4.3 ...

  7. atomic原子类实现机制_深入了解Java atomic原子类的使用方法和原理

    在讲atomic原子类之前先看一个小例子: public class UseAtomic { public static void main(String[] args) { AtomicIntege ...

  8. atomic原子类实现机制_并发编程:并发操作原子类Atomic以及CAS的ABA问题

    本文基于JDK1.8 Atomic原子类 原子类是具有原子操作特征的类. 原子类存在于java.util.concurrent.atmic包下. 根据操作的数据类型,原子类可以分为以下几类. 基本类型 ...

  9. (转)Java atomic原子类的使用方法和原理(一)

    在讲atomic原子类之前先看一个小例子: public class UseAtomic { public static void main(String[] args) { AtomicIntege ...

最新文章

  1. 【linux驱动】嵌入式 Linux 对内存的直接读写(devmem)
  2. 影著协公布的使用费收取标准
  3. Citrix Provisioning Services 7.13企业实战
  4. c++读取txt文件中的数字_在Python中读取包中的数据文件的三种方式
  5. Hyperledger Fabric 实战(七):链码 shim API 详解
  6. 简明批处理教程(2)
  7. phpstudy 升级mysql 及MySQL服务等问题
  8. 事业单位考试高频考点一:马克思主义基本原理
  9. linux拷贝不是某个后缀的文件夹,linux下怎么把一个文件复制到多个文件夹里
  10. 史上最强的GPS基础知识集合
  11. struts2拦截器
  12. java openxml word_C#采用OpenXml实现给word文档添加文字
  13. 多弹多约束协同制导问题
  14. 脉动计算机没有指令计数器,脉冲计数器电路图设计(三) - 脉冲计数器电路图大全(六款脉冲计数器电路设计原理图详解)...
  15. Android APP升级时解析程序包时出现问题
  16. 兴寿镇“春踏青,兴寿行”特色旅游线路点靓辛庄
  17. 【零基础Eviews实例】02自相关(序列相关)的检验与修正
  18. 从四个维度谈谈如何做好团队管理
  19. Fileupload使用方法
  20. _parameter;@param,refid是什么意思?

热门文章

  1. Python只需要三十行代码,打造一款简单的人工语音对话
  2. 八十一、最快最优的快速排序和优化
  3. 四十八、面试前,必须搞懂Java中的线程池(下篇)
  4. pytorch functional
  5. Transformer性能被高估?DeepMind动态评估模型的时间泛化能力
  6. 阿里云机器学习PAI构建AI集团军作战,联手Intel在AI软硬件领域发力
  7. 思而后言:用点赞数据来帮助对话生成模型
  8. 综述:Image Caption 任务之语句多样性
  9. 变分自编码器VAE:一步到位的聚类方案
  10. java hbase 批量查询数据_HBase根据Rowkey批量查询数据JAVA API(一次查多条,返回多个记录)...