【JavaEE】——CAS指令和ABA问题

阿华代码,不是逆风,就是我疯

你们的点赞收藏是我前进最大的动力!!

希望本文内容能够帮助到你!!

目录

一:CAS指令

1:概念

2:伪代码例子说明

3:优点

二:原子类

1:引入

2:代码示例

3:与volatile的区别

4:标准库源码

三:CAS是如何避免线程安全问题

四:CAS中ABA问题

1:引入

2:极端情况

3:解决方案


一:CAS指令

1:概念

CAS是CPU中的一条特殊的指令,它的功能就是完成"比较和交换"

2:伪代码例子说明

伪代码:只能表示一种逻辑,并不能实现编译执行

注:CAS指令一般只关注内存当中的值,寄存器当中的值是多少不打紧,用完就不要了

3:优点

CAS指令不涉及锁,也能保证线程的安全

二:原子类

1:引入

在Java中:

先是操作系统对指令封装成api

然后JVM在对api进行封装,把CAS的api放到了unsafe这个包里(注:这个包里指令会涉及一些系统底层的内容,使用的话是风险操作)

Java标准库中,对CAS再进一步封装,提供了一些工具类,其中最主要的一个工具叫"原子类"

java.util.concurrent.atomic

2:代码示例

我们还是沿用【JavaEE】------线程的安全问题和解决方式_java线程安全 线程不安全-CSDN博客

这篇文章中,用两个线程去自增到10000计数器这个例子

前面我们是通过synchronized加锁方式解决,这里我们使用原子类进行代码编译

java 复制代码
package thread;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: Hua YY
 * Date: 2024-09-29
 * Time: 14:15
 */
public class ThreadDemon36 {

   // private static int result = 0;
    //括号里的参数就是result的初始值
    private static AtomicInteger result = new AtomicInteger(0);
    public static void main(String[] args) throws InterruptedException {

        Thread t1 = new Thread(() -> {
           for(int i = 1 ; i <= 5000 ; i++){
               result.getAndIncrement();//自增相当于result++
               //result++;

           }
        });
        Thread t2 = new Thread(() -> {
            for(int i = 1 ; i <= 5000 ; i++){
                result.getAndIncrement();//自增
                //result++;
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println(result.get());//获取到result中持有的值
    }
}

这里的++操作,不在是(load,add,save)三条指令了,而是打包成了CAS指令,成为一个天然的原子性指令,这样就避免了,多线程中两者的指令相互穿插执行,也就避免了线程安全问题

(1)方法总结

①.getAndIncrement()------相当于count++

②.incrementAndGet()------相当于++count

3:与volatile的区别

volatile是禁止指令重排序(因为操作非原子性嘛),

4:标准库源码

三:CAS是如何避免线程安全问题

核心点:就是通过CAS和while循环的搭配,来确保内存中的值和寄存器当中的值是一样的,

这里的代价就是"自旋"------while循环嘛,但是一般循环不了几次就OK了,这点资源损耗可以忽略

不计,CAS还是很香的~~~

四:CAS中ABA问题

1:引入

上述图文看明白之后,我们可以总结出一点,CAS判断内存和寄存器中的值是否相等,本质上就是在判断------是否有其他线程穿插指令

想象一下,在CAS之前,如果有一个线程穿插进去把数值修改了,紧接着第二个线程也穿插进去把错误的数值又修改回来了,那么CAS是感知不到有线程穿插进来的(如穿~~)

一般来说这也不会引起什么bug之类的

2:极端情况

存钱

3:解决方案

(1)约定数据变化本身就是单向的,只能增加或者只能减少

(2)对于本身必须就是双向变化的数据,引入一个版本号------这个版本号就是只能增加不能减少

相关推荐
大飞pkz4 分钟前
【设计模式】题目小练2
开发语言·设计模式·c#·题目小练
啟明起鸣9 分钟前
【网络编程】从与 TCP 服务器的对比中探讨出 UDP 协议服务器的并发方案(C 语言)
服务器·c语言·开发语言·网络·tcp/ip·udp
007php00717 分钟前
Redis高级面试题解析:深入理解Redis的工作原理与优化策略
java·开发语言·redis·nginx·缓存·面试·职场和发展
九章云极AladdinEdu31 分钟前
深度学习优化器进化史:从SGD到AdamW的原理与选择
linux·服务器·开发语言·网络·人工智能·深度学习·gpu算力
axban32 分钟前
QT M/V架构开发实战:QStandardItemModel介绍
开发语言·数据库·qt
Yeats_Liao35 分钟前
Spring缓存(二):解决缓存雪崩、击穿、穿透问题
java·spring·缓存
猿究院-赵晨鹤1 小时前
String、StringBuffer 和 StringBuilder 的区别
java·开发语言
I'm a winner1 小时前
第五章:Python 数据结构:列表、元组与字典(一)
开发语言·数据结构·python
葵野寺1 小时前
【RelayMQ】基于 Java 实现轻量级消息队列(九)
java·开发语言·rabbitmq·java-rabbitmq
代码不停1 小时前
MySQL联合查询
java·数据库·mysql