IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> CAS了解一下 -> 正文阅读

[Java知识库]CAS了解一下

作者:token keyword

今天咱们还是给自个充充电,就不大战秃头老了!等冲个差不多,电死!

今天我们就来聊一下CAS,这个玩意,太重要了,是并发包(JUC)的基础,没有它可以说是并发包简直就是废废的,说它之前咱们先讨论一下线程不安全的场景,并尝试解决一下!


目的:怎么让一个变量的快速到达200?

/**
 * @author: tianjx
 * @date: 2022/1/2 16:22
 * @description: 线程不安全问题!
 */
public class CASDemo04 {

    // 类的成员变量
    static int data = 0;

    // main方法内代码
    public static void main(String[] args) {
       IntStream.range(0,5).forEach((i)->{
           new Thread(()->{
               IntStream.range(0,40).forEach((j)->{
                   data++;
               });
           }).start();
       });
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(data);
    }
}

在这里插入图片描述

哦?发现用多线程,竟然会造成结果不等于200!(假设A,B线程同时拿到data(100),A快一点,A现在工作内存中改成101,然后写到主内存,然后B开始,先在工作内存改成101,然后在写到主内存101,明明加了两次,但是结果确实101!)

啊!这可怎么办?那肯定就是加锁吧!

方法一:那…synchronized登场吧,我们常用的!

/**
 * @author: tianjx
 * @date: 2022/1/2 16:39
 * @description: synchronized 解决线程不安全
 */
public class CASDemo05 {
    // 类的成员变量
    static int data = 0;

    // main方法内代码
    public static void main(String[] args) {
        IntStream.range(0,5).forEach((i)->{
            new Thread(()->{
                synchronized (CASDemo05.class){
                    IntStream.range(0,40).forEach((j)->{
                        data++;
                    });
                }
            }).start();
        });
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(data);
    }
}

这个确实可以,但是我们都清楚synchronized是悲观锁,它会造成以下缺陷:

1、如果根据时间片来获取锁,加锁,释放锁,再加锁,在释放锁,那么就会造成频繁的上下文切换,线程一多,反而多线程的性能可能还不如单线程!

2、一个线程持有锁之后,所有的线程都会被阻塞或者挂起!这不是假多线程嘛!

(其实synchronized也做过很多的优化,刚刚开始可不是重量级锁哦!如果大家也想了解了解,请双击屏幕点个赞,并且关注不迷路哦!跪谢!)

方法二:lock(朋友莫慌,后面会介绍)

public class CASDemo06 {
    // 类的成员变量
    static int data = 0;
    static Lock lock = new ReentrantLock();

    // main方法内代码
    public static void main(String[] args) {
        IntStream.range(0,5).forEach((i)->{
            new Thread(()->{
                IntStream.range(0,40).forEach((j)->{
                    lock.lock();
                    data++;
                    lock.unlock();
                });
            }).start();
        });
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(data);
    }
}

方法三:AtomicInteger(朋友莫慌,后面会介绍)

/**
 * @author: tianjx
 * @date: 2022/1/2 16:54
 * @description: Atomic
 */
public class CASDemo07 {
    // 类的成员变量
    static AtomicInteger atomicInteger = new AtomicInteger(0);

    // main方法内代码
    public static void main(String[] args) {
        IntStream.range(0,5).forEach((i)->{
            new Thread(()->{
                IntStream.range(0,40).forEach((j)->{
                    atomicInteger.incrementAndGet();
                });
            }).start();
        });
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(atomicInteger.get());
    }
}

方法四:LongAdder(朋友莫慌,后面会介绍)

/**
 * @author: tianjx
 * @date: 2022/1/2 16:58
 * @description:
 */
public class CASDemo08 {
    // 类的成员变量
    static LongAdder longAdder = new LongAdder();

    // main方法内代码
    public static void main(String[] args) {
        IntStream.range(0,5).forEach((i)->{
            new Thread(()->{
                IntStream.range(0,40).forEach((j)->{
                    longAdder.increment();
                });
            }).start();
        });
        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(longAdder);
    }
}
  • Lock底层是用AQS+CAS来实现的,在高并发的场景下,比synchronized性能高的可不是一点半点
  • AtomicIntegerJUC并发包下的,底层实现也是CAS,相对于lock不断加锁,释放锁更加优雅!
  • LongAdder是JDK1.8之后新增的类,也是JUC并发包下的,不用说实现也是用了CAS,不过哦,他很有特点,它比上面更适合在高并发场景下,写的次数大于读的次数!

多线程要保证线程安全可是非常重要的,而且上面的方法多次提到了CAS,我们必须的聊聊了!(可能前奏有点多,但是上面知识可能会在工作中常用,所以就放到前面了!)


先简单介绍下它,CAS其实就是compare and swap的缩写,比较并交换,他是一种乐观锁,也是一种无锁,认为大概率是不需要加锁的,如果需要加锁就进行比较并交换!

接下来我们说下原理,他又三个参数v,o,nv表示内存中实际存放的值,o表示预期的值,n表示要修改的值,如果v==o,那么我们则把值改成n,如果不相等则更改失败,返回o!(就是这么简单,别惊讶!JUC包下的类,大部分都调用的是unsafe的cas方法,可能参数或多或少,基本上都是这个的变种,八九不离十!)


我们已经了解了它并且还知道了它的原理,但是大家有没有一种疑惑!假如又两个线程AB,A线程发现v==o,准备更改为n,但是B线程来了,也发现v==o准备改成另外一个值,这不并没有实现线程安全嘛!这不吹牛那?

是这样的,自从JNI的出现,我们java也可以越过JVM,调用操作系统原语了,而CAS就是一种系统原语,而系统原语就是属于操作系统原语了,操作系统原语可能是若干条指令,但是一个原语是不能被其他终端打断的,所以并不会出现上面的问题!

在这里插入图片描述


最后的最后我们再聊聊CAS有什么优缺点把!

优点:采用无锁的方式,在性能上可能会有不小的提升

缺点:1、会造成ABA问题,2、只能保证一个共享变量(不过可以进行变量合并成对象来玩)


最后的最后的最后在聊聊ABA问题!

臭名昭著的ABA问题,即第一个线程把A把值改成B,然后在改成A,第二个线程发现没有符合期望值,那执行呗!

其实这不完全算问题,有些工作场景并不关心这个,只要最后的值相同就行!比如买了一张火车票,然后秒退,这时候火车票总数不变,不影响!但是有些场景很注重次数,可能这个就很严重了,假如高考试卷,有个人偷偷打开了保险箱,把试题拍了下来,第二个人来的时候是看不出来的,但是这样就造成了泄题!

这时候我们其实加个版本号就行了,每次操作算一个版本,比较值的同时,在比较版本号就行了,具体实现的类有AtomicStampedReference等等!

在这里插入图片描述

参考博客:一文彻底搞懂CAS实现原理_东升的思考-CSDN博客

参考博客:CAS是什么?彻底搞懂CAS_阿杰-CSDN博客_cas

以上只是我的简单理解,如果不足之处,请大家指出!

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-01-03 15:56:35  更:2022-01-03 15:57:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:07:00-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码