iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Java CAS机制详解
  • 353
分享到

Java CAS机制详解

Java CASJava CAS机制 2023-01-28 06:01:29 353人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录一、什么是CAS什么是CAS机制为何CAS如此优秀CAS为什么要和volitile配合使用二、Java中的Atomic原子操作包三、类AtomicInteger四、Unsafe类

一、什么是CAS

什么是CAS机制

CAS机制是一种数据更新的方式。在具体讲什么是CAS机制之前,我们先来聊下在多线程环境下,对共享变量进行数据更新的两种模式:悲观模式和乐观锁模式。

悲观锁更新的方式认为:在更新数据的时候大概率会有其他线程去争夺共享资源,所以悲观锁的做法是:第一个获取资源的线程会将资源锁定起来,其他没争夺到资源的线程只能进入阻塞队列,等第一个获取资源的线程释放锁之后,这些线程才能有机会重新争夺资源。synchronized就是java中悲观锁的典型实现,synchronized使用起来非常简单方便,但是会使没争抢到资源的线程进入阻塞状态**,线程在阻塞状态和Runnable状态之间切换效率较低(比较慢)。比如你的更新操作其实是非常快的,这种情况下你还用synchronized将其他线程都锁住了,线程从Blocked状态切换回Runnable华的时间可能比你的更新操作的时间还要长。**

乐观锁更新方式认为:在更新数据的时候其他线程争抢这个共享变量的概率非常小,所以更新数据的时候不会对共享数据加锁。但是在正式更新数据之前会检查数据是否被其他线程改变过,如果未被其他线程改变过就将共享变量更新成最新值,如果发现共享变量已经被其他线程更新过了,就重试,直到成功为止。CAS机制就是乐观锁的典型实现。

CAS,是Compare and Swap的简称,在这个机制中有三个核心的参数:

  • 主内存中存放的共享变量的值:V(一般情况下这个V是内存的地址值,通过这个地址可以获得内存中的值)
  • 工作内存中共享变量的副本值,也叫预期值:A
  • 需要将共享变量更新到的最新值:B

CAS,compare and swap的缩写,中文翻译成比较并交换。

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。

为何CAS如此优秀

硬件加持,现代大多数处理器都从硬件层面通过一些列指令实现CompareAndSwap(比较并交换)同步原语,进而使操作系统JVM可以直接使用这些指令实现锁和并发数据结构。我们可以简单认为,CAS是将比较和交换合成是一个原子操作。

JVM对CAS的支持, 由于Java程序运行在JVM上,所以应对不同的硬件体系架构的处理则需要JVM来实现。在不支持CAS操作的硬件上,jvm将使用自旋锁来实现。

CAS为什么要和volitile配合使用

cas保证原子性。volitile保证可见性和有序性,二者加起来,保证线程安全

二、Java中的Atomic原子操作包

JUC 并发包中原子类 , 都存放在 java.util.concurrent.atomic 类路径下:

根据操作的目标数据类型,可以将 JUC 包中的原子类分为 4 类:

基本原子类

数组原子类

原子引用类型

字段更新原子类

1. 基本原子类

基本原子类的功能,是通过原子方式更新 Java 基础类型变量的值。基本原子类主要包括了以下三个:

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

2. 数组原子类

数组原子类的功能,是通过原子方式更数组里的某个元素的值。数组原子类主要包括了以下三个:

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

3. 引用原子类(可以奖多个数据聚合成一个对象坐cas操作,但是不要直接修改原对象,而是每次复制处新对象,在新对象生更改后,通过cas设置回去)。

引用原子类主要包括了以下三个:

  • AtomicReference:引用类型原子类
  • AtomicMarkableReference :带有更新标记位的原子引用类型。
  • AtomicStampedReference :带有更新版本号的原子引用类型。

AtomicStampedReference通过引入“版本”的概念,来解决ABA的问题。

4. 字段更新原子类

字段更新原子类主要包括了以下三个:

  • AtomicIntegerFieldUpdater:原子更新整型字段的更新器。
  • AtomicLongFieldUpdater:原子更新长整型字段的更新器。
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。

三、类AtomicInteger

1、常用的方法:

方法 介绍

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) 通过 CAS 方式设置整数值

AtomicInteger 案例:

 private static void out(int oldValue,int newValue){
        System.out.println("旧值:"+oldValue+",新值:"+newValue);
    }
    public static void main(String[] args) {
        int value = 0;
        AtomicInteger atomicInteger= new AtomicInteger(0);
        //取值,然后设置一个新值
        value = atomicInteger.getAndSet(3);
        //旧值:0,新值:3
        out(value,atomicInteger.get());
        //取值,然后自增
        value = atomicInteger.getAndIncrement();
        //旧值:3,新值:4
        out(value,atomicInteger.get());
        //取值,然后增加 5
        value = atomicInteger.getAndAdd(5);
        //旧值:4,新值:9
        out(value,atomicInteger.get());
        //CAS 交换
        boolean flag = atomicInteger.compareAndSet(9, 100);
        //旧值:4,新值:100
        out(value,atomicInteger.get());
    }

2、AtomicInteger 源码解析

public class AtomicInteger extends Number implements java.io.Serializable {
    // 设置使用Unsafe.compareAndSwapint进行更新
    private 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;
    //自动设置为给定值并返回旧值。
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }
    //以原子方式将当前值加1并返回旧值。
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }
    //以原子方式将当前值减1并返回旧值。
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }
    //原子地将给定值添加到当前值并返回旧值。
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }
    ...省略
}

通过源码我们发现AtomicInteger的增减操作都调用了Unsafe 实例的方法,下面我们对Unsafe类做介绍:

四、Unsafe类

Unsafe 是位于 sun.misc 包下的一个类,Unsafe 提供了CAS 方法,直接通过native 方式(封装 c++代码)调用了底层的 CPU 指令 cmpxchg。

Unsafe类,翻译为中文:危险的,Unsafe全限定名是 sun.misc.Unsafe,从名字中我们可以看出来这个类对普通程序员来说是“危险”的,一般应用开发者不会用到这个类。

1、Unsafe 提供的 CAS 方法

主要如下: 定义在 Unsafe 类中的三个 “比较并交换”原子方法


public final native boolean compareAndSwapObject(Object o, long offset, Object expected, Object update);
public final native boolean compareAndSwapInt( Object o, long offset, int expected,int update);
public final native boolean compareAndSwapLong( Object o, long offset, long expected, long update);

Unsafe 提供的 CAS 方法包含四个入参: 包含要修改的字段对象、字段内存位置、预期原值及

新值。在执行 Unsafe 的 CAS 方法的时候,这些方法首先将内存位置的值与预期值(旧的值)比

较,如果相匹配,那么处理器会自动将该内存位置的值更新为新值,并返回 true ;如果不相匹配,

处理器不做任何操作,并返回 false 。

2、获取属性偏移量

Unsafe 提供的获取字段(属性)偏移量的相关操作,主要如下:

 
public native long staticFieldOffset(Field field); 
public native long objectFieldOffset(Field field);

staticFieldOffset 方法用于获取静态属性 Field 在 Class 对象中的偏移量,在 CAS 操作静态属性时,会用到这个偏移量。

objectFieldOffset 方法用于获取非静态 Field (非静态属性)在 Object 实例中的偏移量,在 CAS 操作对象的非静态属性时,会用到这个偏移量。

3、根据属性的偏移量获取属性的最新值:


public native int getIntVolatile(Object o, long fieldOffset);

五、CAS的缺点

ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。

jdk 提供了两个类 AtomicStampedReference、AtomicMarkableReference 来解决 ABA 问题。

只能保证一个共享变量的原子操作。一个比较简单的规避方法为:把多个共享变量合并成一个共享变量来操作。 JDK 提供了 AtomicReference 类来保证引用对象之间的原子性,可以把多个变量放在一个 AtomicReference 实例后再进行 CAS 操作。比如有两个共享变量 i=1、j=2,可以将二者合并成一个对象,然后用 CAS 来操作该合并对象的 AtomicReference 引用。

循环时间长开销大。高并发下N多线程同时去操作一个变量,会造成大量线程CAS失败,然后处于自旋状态,导致严重浪费CPU资源,降低了并发性。

解决 CAS 恶性空自旋的较为常见的方案为:

分散操作热点,使用 LongAdder 替代基础原子类 AtomicLong。

使用队列削峰,将发生 CAS 争用的线程加入一个队列中排队,降低 CAS 争用的激烈程度。JUC 中非常重要的基础类 AQS(抽象队列同步器)就是这么做的。

六、以空间换时间LongAdder 

1、LongAdder 的原理

LongAdder 的基本思路就是分散热点, 如果有竞争的话,内部维护了多个Cell变量,每个Cell里面有一个初始值为0的long型变量, 不同线程会命中到数组的不同Cell (槽 )中,各个线程只对自己Cell(槽) 中的那个值进行 CAS 操作。这样热点就被分散了,冲突的概率就小很多。 在没有竞争的情况下,要累加的数通过 CAS 累加到 base 上。 如果要获得完整的 LongAdder 存储的值,只要将各个槽中的变量值累加,后的值即可。

七、使用AtomicStampedReference解决ABA问题

JDK 的提供了一个类似 AtomicStampedReference 类来解决 ABA 问题。

AtomicStampReference 在 CAS 的基础上增加了一个 Stamp 整型 印戳(或标记),使用这个印戳可以来觉察数据是否发生变化,给数据带上了一种实效性的检验。

AtomicStampReference 的 compareAndSet 方法首先检查当前的对象引用值是否等于预期引用, 并且当前印戳( Stamp )标志是否等于预期标志,如果全部相等,则以原子方式将引用值和印戳 ( Stamp )标志的值更新为给定的更新值。 1、AtomicStampReference 的构造器:

/**  
* @param initialRef初始引用  
* @param initialStamp初始戳记  
*\ 
AtomicStampedReference(V initialRef, int initialStamp)

2、AtomicStampReference 的常用的几个方法如下:

方法 介绍

public V getRerference() 引用的当前值 public int getStamp() 返回当前的"戳记"

public boolean weakCompareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp)
expectedReference 引用的旧值
newReference 引用的新值
expectedStamp 旧的戳记
newStamp 新的戳记  
    public static void main(String[] args) {
        boolean success = false;
        AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(1, 0);
        int stamp = atomicStampedReference.getStamp();
        success = atomicStampedReference.compareAndSet(1, 0, stamp, stamp + 1);
        System.out.println("success:" + success + ";reference:" + "" + atomicStampedReference.getReference() + ";stamp:" + atomicStampedReference.getStamp());
        //修改印戳,更新失败
        stamp = 0;
        success = atomicStampedReference.compareAndSet(0, 1, stamp, stamp + 1);
        System.out.println("success:" + success + ";reference:" + "" + atomicStampedReference.getReference() + ";stamp:" + atomicStampedReference.getStamp());
    }

八、AtomicIntegerFieldUpdater进行字段更新

如果一个类是自己编写的,则可以在编写的时候把成员变量定义为Atomic类型。但如果是一个已经有的类,在不能更改其源代码的情况下,要想实现对其成员变量的原子操作,就需要AtomicIntegerFieldUpdater、AtomicLongFieldUpdater 和 AtomicReferenceFieldUpdater。

    public static void main(String[] args) {
        Student student = new Student();
        //创建AtomicIntegerFieldUpdater对象
        AtomicIntegerFieldUpdater<Student> studentAtomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Student.class, "age");
        //打印age并将age+1
        System.out.println(studentAtomicIntegerFieldUpdater.getAndIncrement(student));
        System.out.println(student.age);
    }
//测试类   
public class Student {
	//因为是用反射实现的这里必须要使用public修饰
    public volatile int  age;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

要修改的age必须是int不能是包装类Integer,必须被volatile修饰

if (field.getType() != int.class)
throw new IllegalArgumentException("Must be integer type");
if ( ! Modifier.isVolatile(modifiers))
throw new IllegalArgumentException( "Must be volation type");

AtomicIntegerFieldUpdater代码实现

	//AtomicIntegerFieldUpdater的无参构造被protected修饰,使用newUpdater创建对象
    protected AtomicIntegerFieldUpdater() {
    }
    public static <U> AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,
                                                              String fieldName) {
        return new AtomicIntegerFieldUpdaterImpl<U>
            (tclass, fieldName, Reflection.getCallerClass());
    }
        public final int getAndIncrement(T obj) {
        	//age+1
            return getAndAdd(obj, 1);
        }
        public final int getAndAdd(T obj, int delta) {
        	//检查obj和声明AtomicIntegerFieldUpdater时的class一不一样
            accessCheck(obj);
            //Unsafe类获得age的值并且使用compareAndSwapInt将age+1
            return U.getAndAddInt(obj, offset, delta);
        }

AtomicLongFieldUpdater 和 AtomicReferenceFieldUpdater和AtomicIntegerFieldUpdater类似

到此这篇关于Java CAS机制详解的文章就介绍到这了,更多相关Java CAS内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Java CAS机制详解

本文链接: https://www.lsjlt.com/news/178160.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Java CAS机制详解
    目录一、什么是CAS什么是CAS机制为何CAS如此优秀CAS为什么要和volitile配合使用二、Java中的Atomic原子操作包三、类AtomicInteger四、Unsafe类...
    99+
    2023-01-28
    Java CAS Java CAS机制
  • Java的锁机制:synchronized和CAS详解
    目录一为什么要用锁二synchronized怎么实现的三CAS来者何人四synchronized和CAS孰优孰劣轻量级锁重量级锁总结提到Java的知识点一定会有多线程,JDK版本不断...
    99+
    2022-11-12
  • Java CAS机制的一些理解
    目录多线程实践什么是CAS机制为何AtomicInteger线程安全图解CAS机制ABA问题什么是ABA问题有什么影响解决总结多线程实践 public class test { ...
    99+
    2022-11-12
  • 全面了解Java中的CAS机制
    前言在看到Java锁机制的时候,无意中看到了CAS这个词,然后在百度查找CAS看了很多文章始终没有看的太懂,今天又在Google上查找了一些资料,才算是真正弄清楚了CAS机制。什么是CAS在jdk 1.5中增加的一个最主要的支持是Atomi...
    99+
    2023-05-31
    java cas 机制
  • 详解JAVA如何实现乐观锁以及CAS机制
    目录前言问题引入悲观锁解决乐观锁解决乐观锁改进CAS机制总结前言 生活中我们看待一个事物总有不同的态度,比如半瓶水,悲观的人会觉得只有半瓶水了,而乐观的人则会认为还有半瓶水呢。很多技...
    99+
    2022-12-08
    JAVA乐观锁 CAS机制 JAVA乐观锁 JAVA CAS
  • Java多线程 乐观锁和CAS机制详细
    目录一、悲观锁和乐观锁1、悲观锁2、乐观锁二、CAS机制一、悲观锁和乐观锁 1、悲观锁 悲观锁是基于一种悲观的态度类来防止一切数据冲突,它是以一种预防的姿态在修改数据之前把数据锁住,...
    99+
    2022-11-12
  • Java多线程之并发编程的基石CAS机制详解
    目录一、CAS机制简介1.1、悲观锁和乐观锁更新数据方式1.2、什么是CAS机制1.3、CAS与sychronized比较1.4、Java中都有哪些地方应用到了CAS机制呢?...
    99+
    2022-11-12
  • 详解java 中的CAS与ABA
    目录1. 独占锁: 1.1 乐观锁的操作 2. 乐观锁: 2.1 CAS操作 3. 原子变量类 4. CAS的缺陷 1. 独占锁: 属于悲观锁,有共享资源,需要加锁时,会以独占锁的...
    99+
    2022-11-12
  • Java并发之CAS原理详解
    目录开端1.代码1.1修改后的代码1.2代码改进:CAS模仿2.CAS分析2.1Java对CAS的支持2.2CAS实现原理是什么?2.3CAS存在的问题2.3.1什么是ABA问题?2...
    99+
    2022-11-13
  • Java中 CAS机制的原理是什么
    Java中 CAS机制的原理是什么?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。多线程实践public class test { &nb...
    99+
    2023-06-15
  • 浅谈Java非阻塞同步机制和CAS
    目录什么是非阻塞同步悲观锁和乐观锁CAS什么是非阻塞同步 非阻塞同步的意思是多个线程在竞争相同的数据时候不会发生阻塞,从而能够在更加细粒度的维度上进行协调,从而极大的减少线程调度的开...
    99+
    2022-11-12
  • JAVA怎么实现乐观锁及CAS机制
    本篇内容介绍了“JAVA怎么实现乐观锁及CAS机制”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前言生活中我们看待一个事物总有不同的态度,比...
    99+
    2023-07-04
  • Java反射机制详解
    目录类的声明周期不同阶段都可以获取类对象获取Class类对象的方式的场景class类对象的功能如何获取私有变量的值根据有无主方法判断进程和线程反射出现的背景(记住)反射出现的背景类的...
    99+
    2022-11-13
  • 详解从ObjectPool到CAS指令
    目录源码解析私有字段构造方法Get 方法Return 方法关于 Interlocked.CompareExchange总结相信最近看过我的文章的朋友对于Microsoft.Exten...
    99+
    2022-11-13
    ObjectPool到CAS指令 CAS指令
  • Java 基础 - 注解机制详解
    注解是JDK1.5版本开始引入的一个特性,用于对代码进行说明,可以对包、类、接口、字段、方法参数、局部变量等进行注解。它是框架学习和设计者必须掌握的基础。@pdaiJava 基础 - 注解机制详解注解基础Java内置注解内置注解 - @Ov...
    99+
    2022-12-02
    java框架 java全栈 java学习路线 java全栈知识 java面试 知识体系 java技术体系 java编程
  • java反射机制最详解
    目录java反射机制什么是反射?反射的功能:反射常用类:1.Class枚举类2.Constructor构造器3.Method方法类4.Field变量类反射运行指示图通过反射获取对象总结java反射机制 什么是反射? 在java开发中有一个...
    99+
    2020-08-07
    java java反射机制
  • Java包机制及javadoc详解
    包机制 为了更好地组织类,Java提供了包机制,用于区别类名的命名空间包语法的语法格式为: package pkg1[. pkg2[. pkg3...]]; 一般利用公司域名倒置作为...
    99+
    2022-11-13
    Java包机制 javadoc命令
  • Java Proxy机制详细解读
     动态代理其实就是java.lang.reflect.Proxy类动态的根据您指定的所有接口生成一个class byte,该class会继承Proxy类,并实现所有你指定的接口(您在参数中传入的接口数组);然后再利用您指定的cla...
    99+
    2023-05-31
    java proxy机制 rox
  • Java-反射机制(超详解)
    Java反射机制概述 前言一、Java反射机制概述1. Java Reflection2. 动态语言 vs 静态语言 二、 Class类的理解1. 类的加载过程1.1 初步了解1.2 类的加载过程图解1.3 了解:什么时候会发...
    99+
    2023-08-17
    java 开发语言
  • Java CAS与Atomic原子操作核心原理详解
    目录什么是原子操作CAS相关原子操作类的使用AtomicIntegerAtomicIntegerArray更新引用类型原子更新字段类LongAdder什么是原子操作 Mysql事务中...
    99+
    2023-05-16
    Java CAS与Atomic原子操作 Java CAS原子操作 Java Atomic原子操作
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作