27. 深入理解CAS

简介: 27. 深入理解CAS

深入理解CAS

什么是CAS?

CAS是原子类的一个方法:compareAndSet  CAS是CPU的并发原语,该方法有两个参数,返回类型为布尔值,操作成功返回true,否则返回false

public final boolean compareAndSet(int expect, int update) { //期望,修改
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
package com.wyh.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * @program: JUC
 * @description: CAS测试
 * @author: 魏一鹤
 * @createDate: 2022-03-13 20:26
 **/
//什么是CAS? 原子类的一个方法compareAndSet 比较并交换
public class CASDemo {
public static void main(String[] args){
//创建原子类 初始值为2022
        AtomicInteger atomicInteger=new AtomicInteger(2021);
//public final boolean compareAndSet(int expect, int update)  期望 更新
        //如果期望的值达到了,就更新值,否则就不更新 返回布尔值
        System.out.println(atomicInteger.compareAndSet(2021,1999));//true
        //输出结果
        System.out.println(atomicInteger.get()); //1999
//由于当前值已经为1999,无法达到2021 所以改变不成功
        System.out.println(atomicInteger.compareAndSet(2021,1999));//false
        System.out.println(atomicInteger.get()); //1999
    }
}

true

1999

false

1999

UnSafe类

Java是不能调用内存的,c++可以调用内存,navite和uUnSafe类都是java调用c++调用内存的工具

image.png

以下为unsafe中的自增方法 是一个自旋锁


image.png

网络异常,图片无法展示
|

CAS简单总结

比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么久执行,如果不是就一直循环

缺点:

1 由于底层是自旋锁,如果期望值达不到的时候回死循环耗时

2 一次性只能保证一个共享变量的原子性

3 存在ABA问题


  1. CAS的ABA问题(狸猫换太子)

什么是CAS的ABA问题

简单来说,就是两个线程共享一个资源,线程A正常获取,线程B获取之后进行资源改动,但是并未同步告诉给A线程,这时候线程A不知道线程B做了改动,还以为这个资源是原来的资源

类似于乐观锁

package com.wyh.cas;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * @program: JUC
 * @description: CAS测试
 * @author: 魏一鹤
 * @createDate: 2022-03-13 20:26
 **/
//什么是CAS? 原子类的一个方法compareAndSet 比较并交换
public class CASDemo {
public static void main(String[] args){
//创建原子类 初始值为2022
        AtomicInteger atomicInteger=new AtomicInteger(2021);
        //===========捣乱的线程=============
        System.out.println(atomicInteger.compareAndSet(2021,2022));//true
        System.out.println(atomicInteger.get()); //1999
        System.out.println(atomicInteger.compareAndSet(2022,2021));
        System.out.println(atomicInteger.get());
//===========期望的线程=============
        System.out.println(atomicInteger.compareAndSet(2021,6666));
        System.out.println(atomicInteger.get());
    }
}

true

2022

true

2021

true

6666

如何解决这种ABA,就是线程故意捣乱的问题呢,使用原子引入

使用原子引入解决CAS ABA问题

带版本号的原子操作 AtomicStampedReference 第一个参数是期望值,第二个参数是版本号,每次操作的时候先获取版本号,这种思想也是乐观锁的思想

public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
package com.wyh.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
 * @program: JUC
 * @description: CAS测试
 * @author: 魏一鹤
 * @createDate: 2022-03-13 20:26
 **/
//什么是CAS? 原子类的一个方法compareAndSet 比较并交换
public class CASDemo {
public static void main(String[] args){
        //创建原子印用 带版本号的原子操作 第一个参数是期望值,第二个参数是版本号
        //如果泛型是一个包装类,要注意对象的引入问题,在正常业务种 这里面的泛型是一个对象
        AtomicStampedReference<Object> atomicInteger = new AtomicStampedReference<>(1,1);
//模拟两个线程去操作
        //线程A
        new Thread(()->{
//每次操作的时候先获取版本号
            int stamp = atomicInteger.getStamp();
            System.out.println("A1的版本:"+stamp);
//休眠2s保证线程执行顺序
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//CAS设置并替换操作
            System.out.println(atomicInteger.compareAndSet(1, 2, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
            System.out.println("A2的版本:"+atomicInteger.getStamp());
            System.out.println(atomicInteger.compareAndSet(2, 1, atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
            System.out.println("A3的版本:"+atomicInteger.getStamp());
        },"A").start();
//线程B
        new Thread(()->{
//每次操作的时候先获取版本号
            int stamp = atomicInteger.getStamp();
            System.out.println("B1的版本:"+stamp);
try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
//CAS操作
            System.out.println(atomicInteger.compareAndSet(1, 6, stamp, stamp + 1));
            System.out.println("B2的版本:"+atomicInteger.getStamp());
            },"B").start();
}
目录
相关文章
|
JSON Java 数据格式
Java使用Hutool工具包生成二维码、验证码、随机数
Java使用Hutool工具包生成二维码、验证码、随机数
3292 0
Java使用Hutool工具包生成二维码、验证码、随机数
|
数据采集 存储 分布式计算
《离线和实时大数据开发实战》(二)大数据平台架构 & 技术概览2
《离线和实时大数据开发实战》(二)大数据平台架构 & 技术概览2
1116 0
《离线和实时大数据开发实战》(二)大数据平台架构 & 技术概览2
|
关系型数据库 MySQL
mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file
mysql: error while loading shared libraries: libncurses.so.5: cannot open shared object file
871 0
|
消息中间件 Kafka API
Kafka Exactly Once 语义实现原理:幂等性与事务消息
Apache Kafka的Exactly-Once语义确保了消息处理的准确性和一致性。通过幂等性和事务消息,Kafka实现了要么全处理要么全不处理的原子性。文章详细解析了Kafka事务的工作流程,包括生产者的幂等性(通过序列号保证),以及事务消息的提交和回滚过程。Kafka事务提供了ACID保证,但存在性能限制,如额外的RPC请求和单生产者只能执行一个事务。此外,事务适用于同集群内的操作,跨集群时原子性无法保证。了解这些原理有助于开发者更好地利用Kafka事务构建可靠的数据处理系统。
443 3
 Kafka Exactly Once 语义实现原理:幂等性与事务消息
报错org.springframework.jdbc.UncategorizedSQLException: Error attempting to get column ‘xxx‘ from resu
报错org.springframework.jdbc.UncategorizedSQLException: Error attempting to get column ‘xxx‘ from resu
报错org.springframework.jdbc.UncategorizedSQLException: Error attempting to get column ‘xxx‘ from resu
|
JavaScript Java 测试技术
基于微信小程序的在线点餐+springboot+vue.js附带文章和源代码设计说明文档ppt
基于微信小程序的在线点餐+springboot+vue.js附带文章和源代码设计说明文档ppt
149 0
|
存储 消息中间件 数据安全/隐私保护
RocketMq Topic创建和删除
RocketMq Topic创建和删除
5469 0
|
Oracle Java 关系型数据库
【问题】Cause: java.sql.SQLException: 不支持的字符集 (在类路径中添加 orai18n.jar): ZHS16GBK
【问题】Cause: java.sql.SQLException: 不支持的字符集 (在类路径中添加 orai18n.jar): ZHS16GBK
|
存储 Java 关系型数据库
springboot整合多数据源的配置以及动态切换数据源,注解切换数据源
springboot整合多数据源的配置以及动态切换数据源,注解切换数据源
2276 0
|
存储 安全 数据安全/隐私保护
RAID0 RAID1 RAID10 RAID5 各需几块盘才可组建
<p><span style="font-size:14px"><br></span></p> <p><span style="font-size:14px"><strong>RAID0 RAID1 RAID10 RAID5 各需几块盘才可组建</strong><br></span></p> <p></p> <p><span style="font-size:14px"><span
4771 0