Java并发编程基础盘点3 - CAS

Java中CAS(Compare And Swap,比较和交换)算法的技术原理简述 - title: Java中CAS(Compare And Swap,比较和交换)算法的技术原理简述 - date: 2021/8/14 文章目录 CAS全称 Compare And Swap,是一种无锁算法。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。java.util.concurrent包中的原子类就是通过CAS来实现了乐观锁。 这里简要提一下乐观并发策略。 实现线程安全的方法主要有三大手段:阻塞同步(也叫互斥同步),非阻塞同步(基于冲突检测的乐观并发策略)以及无同步方案; 从 阅读详情

CAS(Compare and Swap),中文可以理解为比较并替换,是一种实现并发算法时常用到的技术。它是一种无锁原子算法,CAS是一种乐观锁的实现方式,在操作时是抱着乐观的态度进行的,它总是认为可以成功完成操作。

让我们先直观的理解一下CAS的大体思路:

CAS(V,E,N)

它包含 3 个参数,V表示要更新变量的值,E表示预期值,N表示新值。仅当 V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做过更新,则当前线程则什么都不做。最后,CAS 返回当前V的真实值。

先从一个简单例子开始:

public class CasTest0 {
    private static volatile int m = 0;
    public static void increase1() {
        m++;
    }

    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 1000; i++) {
           new Thread(() -> {
                CasTest0.increase1();
            }).start();
        }
        TimeUnit.SECONDS.sleep(3);
        System.out.println(m);
    }
}

运行这个例子,最后打印出的值可能是任意小于1000的正整数。在之前的文章中讲过,volatile可以保证可见性和有序性,但是无法保证原子性。而m++这一操作又不是原子操作,可以分为三个步骤:

  • 获取静态变量m的值并入栈,int型常量值1入栈

  • 栈顶int型数值相加,并将结果压入栈顶

  • 为静态变量m赋值

从上述分析可得,自增操作并不具有原子性,所以在多线程环境下,运行得到的结果必定小于等于1000。

换成JUC包下的原子类操作试一下:

public class CasTest1 {
    private static AtomicInteger atomicI = new AtomicInteger(0);  
    public static void increase2() {
        atomicI.incrementAndGet(); 
    }

    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                CasTest1.increase2();
            }).start();
        }
        TimeUnit.SECONDS.sleep(3);
        System.out.println(atomicI.get());
    }
}

这样运行结果会始终返回1000,这就取决于原子Integer类AtomicInteger发挥了作用。

反编译看一下实际执行的指定,这里是仅以一条指令完成了自增的操作

看一下AtomicInteger类的实现:

在AtomicInteger中使用了Unsafe类,这个类可以说是Java提供的一个后门类,可以用来直接操作内存地址;

valueOffset,变量value的地址偏移量,具体赋值是在下面的静态代码块中进行的;

value就是需要修改的值,相当于i++操作中的i。

incrementAndGet最终调用Unsafe类中的方法:

//获取内存地址为obj+offset的变量值, 并将该变量值加上delta
public final int getAndAddInt(Object obj, long offset, int delta) {
    int v;
    do {
        //通过对象和偏移量获取变量的值
        //由于volatile的修饰, 所有线程看到的v都是一样的
        v= this.getIntVolatile(obj, offset);
    } while(!this.compareAndSwapInt(obj, offset, v, v + delta));
    return v;
}

具体流程:

1、while循环中的compareAndSwapInt()方法尝试修改v的值, 该方法会通过obj和offset获取变量的值;

2、如果这个值和v不一样,说明其他线程修改了obj+offset地址处的值,此时compareAndSwapInt()返回false,继续循环;

3、如果这个值和v一样,说明没有其他线程修改obj+offset地址处的值,此时可以将obj+offset地址处的值改为v+delta, compareAndSwapInt()返回true,退出循环。

compareAndSwapInt是一个native方法,调用了C++中的方法,后续调用链为调用汇编中的cmpxchg指令,最终通过二进制硬件支持实现了这一原子操作。

那么,CAS都有什么应用场景呢?典型场景就是电商中对于货物的库存的管理。首先从数据库中读取库存,在卖出货物后更新库存时,判断库存数量是否还和自己取出时相同,如果相同则更新,不同则进行自旋直到执行成功。

说了这么多,那么CAS就是完美的吗,很遗憾并不是,CAS仍然存在经典的ABA问题:

按照我们之前的理解,CAS需要检查操作值有没有发生改变,如果没有发生改变则更新。但是存在这样一种情况:如果一个值原来是A,变成了B,然后又变成了A,那么在CAS检查的时候会发现没有改变,但是实质上它已经发生了改变,这就是所谓的ABA问题。

public class CasTest2 {
    private static AtomicInteger atomicI = new AtomicInteger(100);

    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+":"+atomicI.compareAndSet(100, 110));
        },"thread1");
        t1.start();

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName()+":"+atomicI.compareAndSet(110, 100));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread2");
        t2.start();

        Thread t3 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
                System.out.println(Thread.currentThread().getName()+":"+atomicI.compareAndSet(100, 90));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"thread3");
        t3.start();
    }
}

运行结果:

三个线程的运行结果都为true,但是thread3执行时候检查的值是已经被中途修改过的,而不是初始值了。

应对ABA问题,其解决方案是加上版本号,即在每个变量都加上一个版本号,每次改变时加1。

即将原来的:A —> B —> A

变成:1A —> 2B —> 3A

这里引入AtomicStampedReference这个类,它内部不仅维护了对象值,还维护了一个int类型的stamp值,可以将其理解为时间戳或版本号。当AtomicStampedReference对应的数值被修改时,除了更新数据本身外,还必须要更新这个stamp的值。并且当AtomicStampedReference设置对象值时,对象值以及stamp值都必须满足期望,写入才会成功。因此,即使对象值被反复读写,写回原值,只要stamp的值发生变化,就能防止不恰当的写入。


public class CasTest3 {
    private static AtomicStampedReference asr = new AtomicStampedReference(100, 1);
    public static void main(String[] args) throws Exception {
        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("1:" + asr.compareAndSet(100, 110, asr.getStamp(), asr.getStamp() + 1) );
            System.out.println("stamp:"+asr.getStamp()+"  value:"+asr.getReference());
            System.out.println("2:" + asr.compareAndSet(110, 100, asr.getStamp(), asr.getStamp() + 1) );
            System.out.println("stamp:"+asr.getStamp()+"  value:"+asr.getReference());
        });

        Thread t2 = new Thread(() -> {
            int stamp = asr.getStamp();
            try {
                TimeUnit.SECONDS.sleep(4);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("3:" + asr.compareAndSet(100, 110, stamp, stamp + 1) );
            System.out.println("stamp:"+asr.getStamp()+"  value:"+asr.getReference());
        });

        t1.start();
        t2.start();
    }
}

运行结果:

Thread2期待的stamp值为1,Reference的值为100。Thread1在每次自增的同时,stamp值加1,所以在经过Thread1两次修改Reference值后,即使与期望的Reference值相同,但stamp值不同,仍然不做任何修改。

CAS缺点:

最后对CAS的缺点进行一下总结,CAS虽然高效地解决了原子操作问题,但是还是存在一些缺陷的,主要表现在三个方面:

1.循环时间太长

如果CAS一直不成功的情况发生,会一直进行自旋操作,会造成大量CPU执行开销。在JUC中有些地方就限制了CAS自旋的次数,例如BlockingQueue的SynchronousQueue。

2.只能保证一个共享变量原子操作

看了CAS的实现过程,可以得出CAS只能针对一个共享变量,如果是多个共享变量情况,只能使用锁来保证原子性了。

3.ABA问题

CAS需要检查操作值有没有发生改变,如果没有发生改变则更新,但是之前提到的ABA问题会造成一定影响,这时只要加上版本号对其进行限定就可以了。

如果您喜欢这篇文章,欢迎关注订阅我的微信公众号 “码农参上”,不定时发布最新文章

公众号后台回复“面试”获取海量大厂面试资料

CAS、CAS自旋、CAS自旋锁、CLH锁与Java AQS:深入理解并发编程核心机制 CAS(Compare and Swap)是一种无锁(Lock-Free)的原子操作,用于在多线程环境下实现数据同步。CLH(Craig, Landin, Hagersten)锁是一种基于链表的队列锁,通过隐式队列管理线程,减少缓存一致性流量。基于CAS自旋实现的锁,线程在获取锁失败时不会阻塞,而是循环尝试。理解这些机制的原理与实现,是编写高效、健壮并发代码的关键。CAS自旋是指线程在CAS失败后循环重试,直到成功。)的核心框架,用于构建锁和同步器(如。AQS提供需子类实现的抽象方法(如。 阅读详情

相关推荐

Java 的并发编程模型:从 synchronized 到 CAS

和CAS是Java并发编程模型中两种重要的机制。简单易用,能够保证线程安全,但在高并发场景下性能较低。CAS通过无锁的方式实现了高效的并发控制,但在某些情况下可能出现ABA问题。在实际开发中,我们需要根据具体的应用场景选择合适的并发控制机制,以提高程序的性能和可靠性。

shrgegrb的博客 663

Java线程(十):CAS

前言 在Java并发包中有这样一个包,java.util.concurrent.atomic,该包是对Java部分数据类型的原子封装,在原有数据类型的基础上,提供了原子性的操作方法,保证了线程安全。下面以AtomicInteger为例,来看一下是如何实现的。 public final int incrementAndGet() { for (;;) { ...

寻道 1万+

Java 并发编程:Java 中的乐观锁与 CAS

CAS,即 “比较并交换”(Compare-And-Swap),是一种用于解决多线程并行情况下性能损耗问题的机制。CAS 操作是一种乐观锁实现,广泛应用于包中的并发类。高效:CAS 是无锁操作,避免了传统锁机制带来的线程切换和上下文切换的开销;无死锁:由于没有使用锁,因此不会出现死锁问题。CAS 的缺点:忙等待:在高并发情况下,CAS 操作可能会不断重试,导致 CPU 资源浪费。

栗筝i的博客 2691

【Java基础】CAS (Compare And Swap) 操作

CAS其实就是Compare And Swap的一个缩写,顾名思义就是比较并交换,其实就是把当前值与你预期的值进行一个比较,是一种用于在多线程环境下实现同步功能的机制。

fumeidonga的博客 3250

【Java并发编程】3.CAS、Lock、读写锁

CAS 什么是原子(Atom)操作: 多线程中的原子操作类似于数据库中的同时执行AB两个语句,要么同时执行成功,要么同时执行失败。 synchronize的不足: syn是基于阻塞的锁机制,颗粒度还是比较大 的。 如果被阻塞的线程优先级很高怎么办。 拿到锁的线程一直不释放锁怎么办。 如果出现大量竞争会消耗CPU,同时带来死锁或其他安全隐患。 用syn也可以实现原子操作不过不太合适,目前...

sowhat 2683

并发编程CAS

对线程1来说,旧的预期值A=10,要修改的新值B=11。此时线程1开始提交更新,首先进行A和地址V的实际值比较(Compare),发现A不等于V的实际值,提交失败。比如一个线程1从内存位置V取出A,这时候另一个线程2也从内存中取出A,并且线程2进行了一些操作变成B,然后线程2又将内存位置V的数据变为A,这时候线程1进行CAS操作发现内存中仍然是A,然后线程1操作成功。ABA问题指的是,在CAS操作中,如果一个值从A变为B,然后再变回A,那么CAS操作无法检测到中间的变化,可能会导致错误的结果。

Mr.xing的博客 1120

Java中CompareAndSwap(CAS)compareAndSwapInt 一些理解,随手记录下

最近在瞅ConcurrentHashMap源码, CAS的这个方法compareAndSwapInt,有些不是很理解. 虽然知道cas的原理,但是对于这个方法还是比较迷惑,这里随手记录下,查了资料以后的理解。 // 最底层是native方法,那就是C语言实现的,目前没看过C源码,这个坟值得一挖. public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); // 使用就个样子 sun

whax 5424

AD24层次原理图设计避坑指南:如何解决端口连接与自动交叉引用问题

本文深入解析AD24层次原理图设计的核心机制,聚焦端口连接错误、自动交叉引用干扰等常见问题。通过对比自上而下与自下而上设计流程,提供端口类型匹配、命名规范等实用解决方案,并指导如何关闭烦人的红色框线视觉干扰,帮助工程师高效避坑,实现复杂系统的模块化设计。

wdx01234567的博客 616

CAS(Compare and Swap)算法介绍、缺陷和解决思路

问题情景:Java中处理原子操作的Atomic系列类(如AtomicInteger,AtomicDouble等)中,处理同步性问题采用的是CAS算法,看了一下感觉算法貌似有点不对劲,然后就搜了搜网上已有的解决思路汇总如下。 鸣谢程序员囧辉,JupiterMouse和水欣分享博文供笔者参考 CAS是什么 提到同步,第一反应我想到的是synchronized加锁。但是当线程很多并发量很大的时候,...

欢迎访问 花落的速度 的博客 5315

遥感常用相关指数和计算方法(植被指数/水体指数/雪和冰指数/矿物指数/地表温度指数/云和阴影指数/农业指数)

遥感常用相关指数和计算方法(植被指数/水体指数/雪和冰指数/矿物指数/地表温度指数/云和阴影指数/农业指数)

m0_64361769的博客 1万+

Java 大视界 -- Java+Spark MLlib 构建智能推荐系统:协同过滤算法实战与优化(441)

Java+Spark MLlib构建智能推荐系统实战 本文详细介绍了基于Java和Spark MLlib构建协同过滤推荐系统的全流程实践方案。主要内容包括: 架构设计:采用离线批处理为主、近实时更新为辅的架构,包含数据采集层、处理层、模型层、推荐生成层、存储层和应用层六大模块,可支撑千万级用户推荐需求。 技术实现: 使用Spark MLlib的ALS算法进行协同过滤模型训练 提供数据清洗、模型训练、推荐生成等核心代码实现 采用MySQL存储推荐结果,支持批量插入和事务保障 生产级优化: 版本化模型存储规避覆

【青云交】华为云云享专家 | 阿里云开发者社区专家博主 技术圈个人影响力前 9 | 博客之星 TOP12 优快云 首位四榜(原力榜 / 作者周榜 / 领军人物 / 综合热榜)榜首,破平台纪录! 苏州地区全榜霸榜,感恩全网十多万粉丝同行! 2540

【并发编程】CAS到底是什么

CAS是Compare-And-Swap(比较并交换)的缩写,是一种轻量级的同步机制,主要用于实现多线程环境下的无锁算法和数据结构,保证了并发安全性。它可以在不使用锁(如synchronized、Lock)的情况下,对共享数据进行线程安全的操作。

陈宝子的博客 9268

服务器被矿工入侵记录

一次服务器被矿工入驻的处理记录

王瑞的技术专栏 1333

JAVA并发编程: CAS和AQS

说起JAVA并发编程,就不得不聊聊CAS(Compare And Swap)和AQS了(AbstractQueuedSynchronizer)。 CAS(Compare And Swap) 什么是CAS CAS(Compare And Swap),即比较并交换。是解决多线程并行情况下使用锁造成性能损耗的一种机制,CAS操作包含三个操作数——内存位置(V)、预期原值(A)和新

Vioao's Blog 4万+

手把手教你用scATAC-seq分析拟南芥根尖染色质可及性(附代码实战)

本文详细介绍了使用scATAC-seq技术分析拟南芥根尖染色质可及性的完整流程,涵盖从实验设计、数据质控、细胞分群到差异分析和多组学整合的实战步骤。通过具体的代码示例,帮助植物研究者掌握单细胞表观组学分析的核心方法,揭示细胞异质性和基因调控动态。

weixin_29291185的博客 265

十三、FLUENT蒸发/冷凝模型实战:从沸腾模拟到常温蒸发的UDF进阶

本文深入探讨FLUENT蒸发/冷凝模型的应用实践,从沸腾模拟到常温蒸发的UDF进阶。通过分析相变模型的核心逻辑、关键陷阱及UDF动态饱和温度场的实现,帮助工程师准确模拟不同场景下的蒸发/冷凝过程。结合太阳能蒸馏器等工程案例,提供优化方案和常见问题解决方案,显著提升模拟精度与效率。

weixin_42548893的博客 571

java并发编程 Atomic、CAS和AQS的介绍

/ 自定义信号量类 class CustomSemaphore {// 构造函数,初始化信号量许可数量 public CustomSemaphore(int permits) {} // 获取许可 public void acquire() throws InterruptedException {} // 释放许可 public void release() {

不知道说什么的博客 1101

抖音无水印视频爬虫实战:从抓包分析到批量下载指定用户作品

抖音作为当前最受欢迎的短视频平台,拥有海量的优质内容。很多用户希望备份自己或他人的作品,但抖音官方并未提供批量下载功能,且直接下载的视频带有平台水印。本文将带领大家从零开始,通过抓包分析、设备模拟、请求签名等核心技术,实现一个完整的抖音无水印视频爬虫系统。本文仅供技术学习和个人备份使用,请勿将爬取的内容用于商业用途或侵犯他人版权,爬取频率请控制合理范围内。目录一、前言二、技术栈概览三、准备工作3.1 环境搭建3.2 抓包分析步骤3.3 关键API发现四、核心技术解密4.1 X-Gorgon签名算法。

2201_76125261的博客 846

Java并发编程-CAS

CAS 的基本思路就是,如果这个地址上的值和期望的值相等,则给其赋予新值,否则不做任何事儿。循环 CAS 就是在一个循环里不断的做 比较且交换( Compare And Swap) 操作,直到成功为止。

千月落 9051

AutoSAR MemMap模块概述及配置参数详解

这个模块是干啥的?它是用来配置Memory Mapping and Compiler Abstraction module。

4508

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

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

【并发编程】什么是CAS?Java是如何实现CAS操作的?

在Java并发编程中,CAS 代表 "Compare-And-Swap"(交换并比较),这是一种用于实现无锁编程的原子操作。CAS操作通常用于多线程环境中,以确保在没有使用传统锁的情况下,能够安全地更新共享数据。CAS是通过CPU级别的原子指令来实现的,这意味着一个CAS操作就是一个原子操作,该操作一旦开始就不能被打断,必须等待它完成。内存值(V):这是要更新的变量的当前值。旧的预期值(A):这是执行CAS操作前,线程预期的内存值。

luming的博客 2144

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Java CAS并发编程解析

在当今的互联网时代,高并发应用的开发已经成为一项基本技能。以一个在线购物平台为例,当用户在高峰时段进行购物时,系统可能会面临大量的并发请求。在这样的场景下,如果数据更新操作没有妥善处理,就很容易出现数据不一致的问题。为了解决这个问题,我们需要介绍Java高并发知识点之Compare-And-Swap(CAS)。在多线程环境中,当多个线程尝试同时修改同一数据时,传统的锁机制可能会导致性能瓶颈。

我是Java程序员廖志伟,感谢朋友们的支持!不定期贡献一篇高品质、过万文字、图文并茂且附有视频解说、满载代码示例注释的良心之作,坚决杜绝粗制乱造。 829

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

并发编程-CAS

说在前面的话 正如我开篇所说,我要整理一些java并发编程的学习文档,这一篇就是第二篇:java中的CAS。 这一篇主要说的CAS的原理,以及java中的CAS的一些应用吧! 欢迎关注和点赞。 开整 CAS是个啥 CAS,compare and swap的缩写,中文翻译成比较并交换。 首先想说的就是:CAS只是一个思想,原理。 本身没有问题,也没有所谓的ABA问题,也没有所谓的乐观锁长时间占用资源的问题。这些问题都是因为我们使用CAS的方式导致的。 不信?看我细细道来… CAS描述起来大概是: 准备一个预期

662

Java并发编程深度解析:掌握CAS、Synchronized与Callable的高效并发之道

本篇文章主要带大家深入了解Java并发编程,了解CAS,Synchronized原理以及Callable接口的概念及使用。

2203_75565612的博客 1268

Java并发编程——CAS算法定义和使用案例

CAS( Compare And Swap) 是一种保障变量操作原子性的无锁算法,可以在多个线程之间实现对共享数据的安全更新。

weixin_44814196的博客 1334
上一篇: Java并发编程基础盘点2-单例模式
下一篇: Spring Boot零配置启动原理
码农参上
博客等级 码龄17年 9050粉丝 77原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值