【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)对于本身必须就是双向变化的数据,引入一个版本号------这个版本号就是只能增加不能减少

相关推荐
凡人的AI工具箱9 分钟前
40分钟学 Go 语言高并发实战:高性能缓存组件开发
开发语言·后端·缓存·架构·golang
大白的编程日记.13 分钟前
【C++笔记】数据结构进阶之二叉搜索树(BSTree)
开发语言·数据结构·c++·笔记
每天一个秃顶小技巧15 分钟前
01.Golang 源码目录结构
开发语言·后端·golang
小柯J桑_15 分钟前
C++:用红黑树封装map与set-1
开发语言·c++·set·map·红黑树
2401_8532757320 分钟前
Java IO 基础知识总结下
java·开发语言
时光の尘20 分钟前
C语言菜鸟入门·关键字·union的用法
运维·服务器·c语言·开发语言·c·printf
我想睡到自然醒₍˄·͈༝·͈˄*₎◞ ̑25 分钟前
【Android】View的解析—滑动篇
android·java
诸神黄昏EX27 分钟前
Android 常用命令和工具解析之内存相关
android·java·开发语言
AitTech28 分钟前
C#动态类型详解:应用场景与注意事项
开发语言·c#
有请小发菜33 分钟前
Python 编程开发(01):Bash 命令行基本操作
开发语言·python·bash