[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寄存器2
	if(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>() {
            @Override
            public 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来包装callable
        Thread thread = new Thread(futureTask);
        thread.start();//线程开始执行任务
        thread.join();
        System.out.println(futureTask.get());//通过futureTask的get方法;来拿到call的返回值
    }
}

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

华为 二层交换机与防火墙连通上网实验

防火墙是一种网络安全设备&#xff0c;用于监控和控制网络流量。它可以帮助防止未经授权的访问&#xff0c;保护网络免受攻击和恶意软件感染。防火墙可以根据预定义的规则过滤流量&#xff0c;例如允许或阻止特定IP地址或端口的流量。它也可以检测和阻止恶意软件、病毒和其他威…

git与gitlab

目录 gitlab 下载与安装 重置管理员密码 gitlab命令 git远程gitlab相关命令 认证 补充 git git 分布式版本控制 安装 git的四个区域与文件的四个状态 使用git 常用命令 git 分布式管理系统 gitlab 企业私有库 github 公网共享库&#xff0c;全球…

hinge loss(损失函数)

Devise模型中用到hinge loss和相似性度量相结合计算损失&#xff0c;其损失如下&#xff1a; 举例说明该损失如何计算 运用公式2&#xff1a;&#xff08;常用公式2&#xff09;Devise模型用到的是公式2 参考&#xff1a;https://blog.csdn.net/weixin_43055882/article/det…

Java新手必看:快速上手FileOutPutStream类

哈喽&#xff0c;各位小伙伴们&#xff0c;你们好呀&#xff0c;我是喵手。运营社区&#xff1a;C站/掘金/腾讯云&#xff1b;欢迎大家常来逛逛 今天我要给大家分享一些自己日常学习到的一些知识点&#xff0c;并以文字的形式跟大家一起交流&#xff0c;互相学习&#xff0c;一…

OpenHarmony实战开发-应用侧调用前端页面函数

应用侧可以通过runJavaScript()方法调用前端页面的JavaScript相关函数。 在下面的示例中&#xff0c;点击应用侧的“runJavaScript”按钮时&#xff0c;来触发前端页面的htmlTest()方法。 前端页面代码。 <!-- index.html --> <!DOCTYPE html> <html> <…

58行代码把Llama 3扩展到100万上下文,任何微调版都适用 | 最新快讯

量子位公众号 QbitAI 堂堂开源之王 Llama 3&#xff0c;原版上下文窗口居然只有……8k&#xff0c;让到嘴边的一句“真香”又咽回去了。 在 32k 起步&#xff0c;100k 寻常的今天&#xff0c;这是故意要给开源社区留做贡献的空间吗&#xff1f; 开源社区当然不会放过这个机会&a…

Llama3-Tutorial之LMDeploy高效部署Llama3实践

Llama3-Tutorial之LMDeploy高效部署Llama3实践 Llama 3 近期重磅发布&#xff0c;发布了 8B 和 70B 参数量的模型&#xff0c;lmdeploy团队对 Llama 3 部署进行了光速支持&#xff01;&#xff01;&#xff01; 书生浦语和机智流社区同学光速投稿了 LMDeploy 高效量化部署 Llam…

对于子数组问题的动态规划

前言 先讲讲我对于这个问题的理解吧 当谈到解决子数组问题时&#xff0c;动态规划(DP)是一个强大的工具&#xff0c;它在处理各种算法挑战时发挥着重要作用。动态规划是一种思想&#xff0c;它通过将问题分解成更小的子问题并以一种递归的方式解决它们&#xff0c;然后利用这些…

【华为】IPSec VPN手动配置

【华为】IPSec VPN手动配置 拓扑配置ISP - 2AR1NAT - Easy IPIPSec VPN AR3NATIPsec VPN PC检验 配置文档AR1AR2 拓扑 配置 配置步骤 1、配置IP地址&#xff0c;ISP 路由器用 Lo0 模拟互联网 2、漳州和福州两个出口路由器配置默认路由指向ISP路由器 3、进行 IPsec VPN配置&…

Redission分布式锁 watch dog 看门狗机制

为了避免Redis实现的分布式锁超时&#xff0c;Redisson中引入了watch dog的机制&#xff0c;他可以帮助我们在Redisson实例被关闭前&#xff0c;不断的延长锁的有效期。 自动续租&#xff1a;当一个Redisson客户端实例获取到一个分布式锁时&#xff0c;如果没有指定锁的超时时…

笔记86:关于【#ifndef + #define + #endif】的用法

当你在编写一个头文件&#xff08;例如 pid_controller.h&#xff09;时&#xff0c;你可能会在多个源文件中包含它&#xff0c;以便在这些源文件中使用该头文件定义的函数、类或其他声明。如果你在多个源文件中都包含了同一个头文件&#xff0c;那么当你将整个工程统一编译&am…

银行卡实名认证API接口快速对接

银行卡实名认证API接口又叫银行卡核验类API接口、银行卡验证类API接口、银联核验类API接口,根据入参字段不同&#xff0c;分银行卡二要素验证API接口&#xff0c;银行卡三要素验证API接口&#xff0c;银行卡四要素验证API接口。其中&#xff0c;银行卡二要素验证API接口是验证开…

锂电池SOH估计 | Matlab实现基于ALO-SVR模型的锂电池SOH估计

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池SOH估计 | Matlab实现基于ALO-SVR模型的锂电池SOH估计 蚁狮优化支持向量机锂电池健康状态SOH估计&#xff1b; 具体流程如下&#xff1b; 1、分析锂离子电池老化数据集&#xff0c;从中选取具有代表电池性能衰减…

【自用】了解移动存储卡的基本信息

前言 本文是看B站视频做的一个简单笔记&#xff0c;方便日后自己快速回顾&#xff0c;内容主要介绍了存储卡基本参数&#xff0c;了解卡面上的数字、图标代表的含义。对于日后如何挑选判断一张存储卡的好坏、判别一张存储卡是否合格有一定帮助。 视频参考链接&#xff1a;【硬…

深入剖析Tomcat(六) Tomcat各组件的生命周期控制

Catalina中有很多组件&#xff0c;像上一章提到的四种容器&#xff0c;载入器&#xff0c;映射器等都是一种组件。每个组件在对外提供服务之前都需要有个启动过程&#xff1b;组件在销毁之前&#xff0c;也需要有个关闭过程&#xff1b;例如servlet容器关闭时&#xff0c;需要调…

OpenNJet应用引擎——云原生时代的Web服务新选择

文章目录 OpenNJet应用引擎——云原生时代的Web服务新选择引言&#xff1a;数字化转型的推动力&#xff1a;OpenNJet应用引擎为什么选择OpenNJet&#xff1f; OpenNJet的核心优势1. 云原生功能增强2. 安全加固3. 代码重构与性能优化4. 动态加载机制5. 多样化的产品形态6. 易于集…

产业空间集聚DO指数计算

1.前言 创始人 :Duranton and Overman&#xff08;2005&#xff09; 目前应用较多的产业集聚度量指数主要基于两类&#xff0c;一是根据不同空间地理单元中产业经济规模的均衡性进行构造&#xff0c;如空间基尼系数与EG指数&#xff1b;二是基于微观企业地理位置信息形成的产业…

嵌入式系统应用-拓展-FLASH之操作 SFUD (Serial Flash Universal Driver)之KEIL应用

这里已经假设SFUD代码已经移植到工程下面成功了&#xff0c;如果读者对SFUD移植还不了解。可以参考笔者这篇文章&#xff1a;SFUD (Serial Flash Universal Driver)之KEIL移植 这里主要介绍测试和应用 1 硬件设计 这里采用windbond 的W25Q32这款芯片用于SFUD测试。 W25Q32是…

LLM⊗KG范式下的知识图谱问答实现框架思想阅读

分享一张有趣的图&#xff0c;意思是在分类场景下&#xff0c;使用大模型和fasttext的效果&#xff0c;评论也很逗。 这其实背后的逻辑是&#xff0c;在类别众多的分类场景下&#xff0c;尤其是在标注数据量不缺的情况下&#xff0c;大模型的收益是否能够比有监督模型的收益更多…

[渗透利器]全能工具=信息收集->漏洞扫描->EXP调用

前言 hxd开发的工具&#xff0c;大致模块有&#xff08;信息收集&#xff0c;漏洞扫描&#xff0c;暴力破解&#xff0c;POC/EXP&#xff0c;常用编码&#xff09; 工具使用 下载后解压 安装环境 pip install -r requirements.txt 注意&#xff0c;该工具继承了两种不同的使…