[Java EE] 多线程(八):CAS问题与JUC包

🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:🍕 Collection与数据结构 (90平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀Java EE(94平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
在这里插入图片描述

1. CAS问题

1.1 什么是CAS

CAS全程compare and swap,字面意思就是比较和交换(与其说是交换,不如说是赋值),在底层会涉及到一下操作.
底层中涉及到的对象有内存和两个寄存器.

  1. 首先比较内存地址中的值和寄存器1的值是否相等.
  2. 如果相等,把寄存器2中的值赋给内存中.
  3. 如果不相等,不进行任何操作
  4. 返回值为是否赋值成功.
    注意:上述的操作为原子操作.

1.2 伪代码

注意:下面的代码不是原子的,只是用于辅助理解CAS的过程,实际的CAS问题在硬件底层中是原子的.

boolean CAS(address,expectValue,swapValue){//address内存中的值,expectValue寄存器1,swapValue寄存器2if(address == expectValue){address = swapValue;return true;}return false;
}

1.3 CAS是如何实现的

简而言之,是因为硬件方面提供了支持,软件层面才可以做到.由CPU提供了上述指令,因此操作系统内核也能够完成这样的操作,之后OS会提供出响应的api,JVM对OS提供出的api进行封装,我们便可以在Java中使用CAS.

1.4 CAS的有哪些应用

1.4.1 实现原子类

标准库中提供了java.util.concurrent.atomic 包,里面的类都是基于这种方式来实现的.
典型的就是AtomicInteger类.其中的getAndIncrement相当于i++操作.

  • 该类的构造方法可以指定一开始变量的初始值.
  • increamentAndGet --> ++i
  • getAndIncrement–> i++
  • decreamentAndGet --> --i
  • getAndDecreament --> i–
  • getAndAdd(10) --> i+=10
import java.util.concurrent.atomic.AtomicInteger;public class Demo26 {public static void main(String[] args) throws InterruptedException {AtomicInteger atomicInteger = new AtomicInteger(0);Thread thread = new Thread(()->{for (int i = 0; i < 50000; i++) {atomicInteger.incrementAndGet();}});Thread thread1 = new Thread(()->{for (int i = 0; i < 50000; i++) {atomicInteger.incrementAndGet();}});thread.start();thread1.start();thread.join();thread1.join();System.out.println(atomicInteger);}
}

运行结果:
在这里插入图片描述
伪代码实现

class AtomicIntegter{private int value;public int getAndIncrement(){int oldValue = value;while (!CAS(value,oldValue,oldValue+1)){oldValue = value;}return oldValue;}
}

总地来说,原子类没有用到任何的加锁操作(是因为CAS没有用到加锁),使得代码的效率更高,但是这种共操作只适用于部分场景,加锁的使用场景还是比原子类更加通用.

1.4.2 实现自旋锁

伪代码实现:

public class SpinLock{private Thread owner = null;public void lock(){while(!CAS(owner,null,Thread.currentThread()){//判断锁是否被占用,没有就使用当前线程赋值}}public void unlock(){this.owner = null;//解锁之后赋值为null}
}

1.5 CAS的ABA问题

1.5.1 什么是ABA问题

现在存在两个线程,t1线程和t2线程,t1线程要想进行CAS,需要进行一下操作:

  • 先读取num的值,记录到oldNum变量中.
  • 使用CAS判定当前num的值是否为A,如果为A,就修改成Z.
    如果有一个线程t2在这个中间对当前num的值进行了修改.只不过就是从A改成了B又一次改回了A.

举例说明:翻新机
这就好比你买来一个新手机,你无法判断这是一个全新的一手手机,还是有一些无良商家对二手机进行了翻新再卖给你.

1.5.2 ABA问题带来的bug

大部分情况下,ABA问题不会造成什么bug,但是不排除出现一些特殊情况会出现bug.

举例说明:钟离去银行取钱
有请助教:钟离,达达利亚

  • 正常的过程:
    t1线程希望从总结金额100中扣款50,这时候t2线程也希望如此,假设t1先进行了CAS操作,t2阻塞等待,t1线程的CAS操作把new值赋值为old-50之后,new值就变为50.之后在t2进行CAS操作的时候,t2就发现new值和old值不一样,就不会进行CAS操作.
    在这里插入图片描述
  • 异常的情况
    在钟离取款的时候,由于达达利亚考虑到钟离每次都可能不带钱,所以在钟离取款的时候,达达利亚又给钟离打了50块钱.在t1线程执行完CAS操作之后,t2还没有执行CAS,这时候进行了打款操作,t2进行CAS操作的时候,认为old值和new值相等,所以t2也进行了一次扣款操作.这时候就产生了bug.
    在这里插入图片描述

1.5.3 解决方案

要给修改的值引入版本号.版本号只加不减,每次操作一次余额之后,版本号+=1.在CAS⽐较数据当前值和旧值的同时,也要⽐较版本号是否符合预期.

  • 在CAS操作在读取旧值的时候,也要读取版本号.
  • 在真正修改的时候:
    • 如果当前版本号和读到的版本号一样的时候,修改数据.
    • 如果当前版本号高于读到的版本号的时候,就操作失败了.

继续拿前面的钟离取款的例子来说明:
在t1线程进行CAS的时候,进行了扣款操作,版本号+1,之后t3线程进行打款,对版本号+1,之后在t2线程CAS的时候,发现当前版本号高于之前读取到的版本号,则操作失败.
在这里插入图片描述

2. JUC包

JUC包,全称java.util.concurrent.其中存放了和多线程相关的组件.

2.1 Callable接口

该接口和Runnable相同,是描述一个任务的,只不过call方法有返回值,而run方法没有返回值.就是相当于给线程封装了一个返回值.
代码实例:计算1+2+3+…+1000的值

  • 不使用Callable的接口
public class Demo28 {private static int sum = 0;public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{int result = 0;for (int i = 0; i < 1000; i++) {result += i;}sum = result;});thread.start();thread.join();System.out.println(sum);}
}
  • 使用Callable接口
  1. 创建一个匿名类,实现Callable接口的call方法,方法中是累加过程,泛型参数表示返回值的类型.
  2. 把callable实例用FutureTask来包装一下,引用设置为futureTask.
  3. 创建线程,构造方法传入futureTask,启动线程.
  4. 之后通过futureTask的get方法去获取到call方法的返回值.
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;public class Demo27 {public static void main(String[] args) throws InterruptedException, ExecutionException {Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int ret = 0;for (int i = 0; i < 5000; i++) {ret++;}return ret;}};//通过匿名内部类的方法重写call方法FutureTask<Integer> futureTask = new FutureTask<>(callable);//通过FutureTask来包装callableThread thread = new Thread(futureTask);thread.start();//线程开始执行任务thread.join();System.out.println(futureTask.get());//通过futureTask的get方法;来拿到call的返回值}
}

运行结果:
在这里插入图片描述

举例说明:
有请助教:香菱,莱欧斯利
今天,莱欧斯利去万名堂吃饭,万名堂的前台招待员为莱欧斯利点好餐之后,就会产生一个菜单(call方法中的一堆东西),之后会生成小票(futureTask),小票一份值后厨联,一份是顾客联,之后后厨联的小票就会传到香菱的手中(把futureTask传给线程),香菱就会在后厨一通输出,在香菱做好之后,莱欧斯利就可以通过顾客联取到餐(get方法).
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/7210.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MaxKB宝塔Docker安装并配置域名访问

准备 Linux系统 bt面板 默认环境LNMP随便装 服务器环境配置最好是4G&#xff0c; 占用硬盘存储大概1G 对于一些海外AI产品的对接需要使用香港或者海外的服务器 安装 在宝塔面板中打开SSH或者你本地使用SSH工具去链接服务器 运行docker命令 前提是放开服务器的8080端口 doc…

【吊打面试官系列】Java高并发篇 - Java 线程池中 submit() 和 execute()方法有什么区别?

大家好&#xff0c;我是锋哥。今天分享关于 【Java 线程池中 submit() 和 execute()方法有什么区别&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; Java 线程池中 submit() 和 execute()方法有什么区别&#xff1f; 两个方法都可以向线程池提交任务&#xff0c…

Redis - Zset 有序集合

前言 它保留了集合不能有重复成员的特点&#xff0c;但与集合不同的是&#xff0c;有序集合中的每个元素都有⼀个唯⼀的浮点类型的分数&#xff08;score&#xff09;与之关联&#xff0c;有序集合中的元素是可以维护有序性的&#xff0c;但这个有序不是⽤下标作为排序依据⽽是…

STM32F4xx开发学习—GPIO

GPIO 学习使用STM32F407VET6GPIO外设 寄存器和标准外设库 1. 寄存器 存储器映射 存储器本身是不具有地址的&#xff0c;是一块具有特定功能的内存单元&#xff0c;它的地址是由芯片厂商或用户分配&#xff0c;给存储器分配地址的过程就叫做存储区映射。给内存单元分配地址之后…

Pytorch实现图片异常检测

图片异常检测 异常检测指的是在正常的图片中找到异常的数据&#xff0c;由于无法通过规则进行识别判断&#xff0c;这样的应用场景通常都是需要人工进行识别&#xff0c;比如残次品的识别&#xff0c;图片异常识别模型的目标是可以代替或者辅助人工进行识别异常图片。 AnoGAN…

存储故障后oracle报—ORA-01122/ORA-01207故障处理---惜分飞

客户存储异常,通过硬件恢复解决存储故障之后,oracle数据库无法正常启动(存储cache丢失),尝试recover数据库报ORA-00283 ORA-01122 ORA-01110 ORA-01207错误 以前处理过比较类似的存储故障case:又一起存储故障导致ORA-00333 ORA-00312恢复存储故障,强制拉库报ORA-600 kcbzib_kcr…

零基础入门篇①② Python标准数据类型--数字

Python从入门到精通系列专栏面向零基础以及需要进阶的读者倾心打造,9.9元订阅即可享受付费专栏权益,一个专栏带你吃透Python,专栏分为零基础入门篇、模块篇、网络爬虫篇、Web开发篇、办公自动化篇、数据分析篇…学习不断,持续更新,火热订阅中🔥专栏订阅地址 👉Python从…

接口测试及常用的接口测试工具(Postman/Jmeter)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 首先&#xff0c;什么是接口呢&#xff1f; 接口一般来说有两种&#xff0c;一种是程序内部的接…

[Flutter]创建一个私有包并使用

在Flutter中创建一个自己的私有组件&#xff08;通常称为包或库&#xff09;&#xff0c;并通过Dart的包管理工具pub进行使用。 一、创建一个新的Flutter包 1.使用命令行创建 使用Flutter命令行工具来创建一个新的包&#xff1a; $ flutter create --templatepackage my_pri…

软件公司为什么很少接二开项目?

前言 很多企业由于原有项目还在继续运营&#xff0c;但原有技术公司不想再合作或者不想再维持整个技术团队等原因&#xff0c;就需要找一个新的软件公司继续维护原有软件系统。但是一接触往往发现很多软件公司拒绝接手第三方的软件项目&#xff0c;这究竟是什么原因呢&#xff…

某东抢购某台脚本-低调

某东抢购某台脚本 小白操作-学习使用 注意&#xff1a; 本文部分变量已做脱敏处理&#xff0c;仅用于测试和学习研究&#xff0c;禁止用于商业用途&#xff0c;不能保证其合法性&#xff0c;准确性&#xff0c;完整性和有效性&#xff0c;请根据情况自行判断。技术层面需要提…

C语言动态内存管理malloc、calloc、realloc、free函数、内存泄漏、动态内存开辟的位置等的介绍

文章目录 前言一、为什么存在动态内存管理二、动态内存函数的介绍1. malloc函数2. 内存泄漏3. 动态内存开辟位置4. free函数5. calloc 函数6. realloc 函数7. realloc 传空指针 总结 前言 C语言动态内存管理malloc、calloc、realloc、free函数、内存泄漏、动态内存开辟的位置等…

JavaScript this 上下文深度探索:综合指南涵盖隐式与显式call、apply、bind、箭头函数、构造函数等用法于多样场景

JavaScript中的this关键字代表函数执行的上下文环境&#xff0c;核心在于确定函数内部访问的当前对象。它根据函数调用方式动态变化&#xff0c;对事件处理、对象方法调用等至关重要。通过.call(), .apply(), .bind()或箭头函数控制this&#xff0c;可确保代码逻辑正确绑定对象…

ROS 2边学边练(43)-- 利用GTest写一个基本测试(C++)

前言 在ROS&#xff08;Robot Operating System&#xff09;中&#xff0c;gtest&#xff08;Google Test&#xff09;是一个广泛使用的C测试框架&#xff0c;用于编写和执行单元测试。这些测试可以验证ROS节点、服务和消息等的正确性和性能。 如果我们需要在写的包中添加测试&…

[redis] redis为什么快

1. Redis与Memcached的区别 两者都是非关系型内存键值数据库&#xff0c;现在公司一般都是用 Redis 来实现缓存&#xff0c;而且 Redis 自身也越来越强大了&#xff01;Redis 与 Memcached 主要有以下不同&#xff1a; (1) memcached所有的值均是简单的字符串&#xff0c;red…

保持 Hiti 证卡打印机清洁的重要性和推荐的清洁用品

在证卡印刷业务中&#xff0c;保持印刷设备的清洁至关重要。特别是对于 Hiti 证卡打印机来说&#xff0c;它们是生产高质量证卡的关键工具。保持设备清洁不仅可以保证打印质量和效率&#xff0c;还可以延长其使用寿命。本文将探讨保持 Hiti 证卡打印机清洁卡的重要性&#xff0…

首届云原生编程挑战赛总决赛亚军比赛攻略(ONE PIECE团队)

关联比赛: 首届云原生编程挑战赛【复赛】实现一个 Serverless 计算服务调度系统 比赛攻略—ONE PIECE团队 代码链接&#xff1a; 初赛&#xff1a;GitHub - czy-gm/containerScheduler: 2020天池首届云原生编程挑战赛亚军-初赛赛道二&#xff08;实现规模化容器静态布局和动…

高项-案例分析万能答案(作业分享)

项目管理&#xff1a;每天进步一点点~ 活到老&#xff0c;学到老 ヾ(◍∇◍)&#xff89;&#xff9e; 何时学习都不晚&#xff0c;加油 一、通用问题原因: 1.项目经理管理经验不足&#xff0c;没有及时发现和解决xx方面的问题。 2.项目管理计划没有得到关键干系人的评审确…

yum常用命令与lrzsz的在线安装

yum命令 yum&#xff08; Yellow dog Updater, Modified&#xff09;是一个在 Fedora 和 RedHat 以及 SUSE 中的 Shell 前端软件包管理器。 基于 RPM 包管理&#xff0c;能够从指定的服务器自动下载 RPM 包并且安装&#xff0c;可以自动处理依赖性关系&#xff0c;并且一次安装…

php基础知识快速入门

一、PHP基本知识 1、php介绍&#xff1a; php是一种创建动态交互性的强有力的服务器脚本语言&#xff0c;PHP是开源免费的&#xff0c;并且使用广泛。PHP是解释性语言&#xff0c;按顺序从上往下执行&#xff0c;无需编译&#xff0c;直接运行。PHP脚本在服务器上运行。 2、ph…