多线程 之 CAS与synchronized的优化过程

news2024/10/6 12:29:12

前言

本篇介绍什么是CAS与synchronized的优化过程,如有错误,请在评论区指正,让我们一起交流,共同进步!


文章目录

  • 前言
  • 1. 什么是CAS?
  • 2. CAS实现的操作
    • 2.1 实现原子类
    • 2.2 实现自旋锁
  • 3. CAS的aba问题
  • 4. synchronized的优化过程
  • 总结

本文开始

1. 什么是CAS?

CAS:英文是compare and swap,比较和交换(一起执行,不能拆分的);
示例:有寄存器A , 寄存器B,内存C
比较寄存器A的值 和 内存C中的值,如果数值相同,就把寄存器B和内存C中的数值进行交换(给内存C赋值);
【注】与寄存器中的值相比,更加关注内存中的值;

图示:在这里插入图片描述

本质:CAS操作,是一条CPU上的指令,通过一条指令完成上述代码功能(上述示例中交换的功能);

2. CAS实现的操作

 CAS操作是一条CPU指令,这可以认为是原子的;
【注】原子指令:不加锁就能保证线程安全;

2.1 实现原子类

1.标准库里提供 AtomInteger 类 (原子类);
这样的原子类能够保证之前介绍的 ++,-- 的操作,线程是安全的;
了解原子类中的前置后置++,–方法;

               //后置++; -》num++;
               num.getAndIncrement();
               //前置++;-》++num
               num.incrementAndGet();
               //前置--;-》--num;
               num.decrementAndGet();
               //后置--; -》num--;
               num.getAndDecrement();

使用原子类实现两个线程,对num进行20000次++;
代码实现:

    public static void main(String[] args) throws InterruptedException {
        AtomicInteger num = new AtomicInteger(0);
        Thread t = new Thread(() -> {
            for (int i = 0; i < 20000; i++) {
                //后置++; -》num++;
                num.getAndIncrement();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 20000; i++) {
                num.getAndIncrement();
            }
        });
        t.start();
        t2.start();
        t.join();
        t2.join();
        //get 获取到数值
        System.out.println(num.get());
    }

在原子类中,++操作是如何执行的,来看一下伪代码;

在这里插入图片描述

注:oldValue 这里可以看作寄存器;value 看作内存中的值;

伪代码执行过程:
如果value 和 oldValue相同,oldValue + 1赋值给value中(相当于++),然后CAS返回true循环结束;
如果value 和 oldValue不相同,CAS直接返回false,再次循环,重新设置;
【注】上述代码中value, 两次都赋值给oldValue, 这种情况是在多线程情况下;value 是成员变量,多个线程同时调用 getAndIncrement方法;

CAS在自增中的作用:
确认当前value是否变过,如果没变过,才自增,如果变过,先更新,再自增;

2.2 实现自旋锁

自旋锁:反复检查当前锁状态,看锁是否解开;

伪代码看一下CAS实现自旋锁:

public class SpinLock {
	private Thread owner = null;
	//当前锁被那个线程持有;
	public void lock(){
		while(!CAS(this.owner, null, Thread.currentThread())){
		}
	}
	public void unlock (){
	this.owner = null;
	}
}

注:this.owner 是当前线程

CAS执行自旋锁执行过程:
1.比较this.owner 与 null;
2.比较this.owner==null,把当前线程引用设置到owner中,相当于加锁完成,循环结束;
3.this.owner != null, CAS直接返回false, 循环继续执行;(循环会不停访问锁是否释放)
【注】CAS:指令级,是读取内存的操作;内存可见性:编译器优化指令(调整指令),把 读内存 的指令调整成 读寄存器的指令;

3. CAS的aba问题

3.1 什么是aba问题 ?
cas的关键就是对比内存与寄存器中的值,通过对比检测内存是否改变过;如果对比的时候发现是相同的,但并不是没有变过,而是从a -》b -》a; 此时就会产生aba问题;

例子:买了一本书,你不能确定他是新书(没有被人买过看过),还是二手书(换了个包装);
【注】CAS只能对比值是否相同,不能确定值,在中间过程中是否改变过;
 
3.2 如何解决aba问题
解决方式:使用版本号解决aba问题;
数据即递增又递减,就需要使用 版本号变量 来控制;相当于每次CAS对比的时候就对比版本号,而不是数值;
【注】约定版本号只能增加,每次修改,都会增加一个版本号;

前提过程:给一个数num, 给一个版本号version,给一个old记录版本号;约定版本号只能递增;
执行:进行CAS操作,比较version与old是否一样,版本号一样old加1,num加1;

	//示例:
	int num = 10;
	int version = 1;
	old = version;
	CAS(version, old, old+1,num++)
	//每次比较版本号,如果一样,old加1,num加1;

4. synchronized的优化过程

4.1 synchronized 关键策略: 锁升级

锁升级过程:① -》② -》③ -》④(从左往右升级)
① 开始无锁状态
② 偏向锁: 开始加锁是偏向锁(程序运行时,jvm优化的手段);
③ 自旋锁:遇到锁竞争就是自旋锁;
④ 重量级锁:锁竞争激烈,就变成重量级锁(交给内核阻塞等待);

1.什么是偏向锁 ?
偏向锁:只是让线程对锁做个 “标记”;(只是标记,并不加锁)
【注】整个代码执行过程中,如果没有别的线程竞争这个锁,原来的线程就不会真加锁;一旦又其他线程尝试加锁,偏向锁立即升级为自旋锁(如果更激烈会再升级为重量级锁),其他线程只能等待;

2.为什么自旋锁要升级为重量级锁?
自旋锁:获取锁快,但是消耗大量CPU;
自旋锁升级为重量级锁,多于线程会在内核里阻塞等待;(阻塞等待:放弃CPU,不再销毁CPU资源,等待系统内核调度)

4.2 锁消除
锁消除:编译阶段做的优化手段;

锁消除的目的:
为了检测当前代码是否是多线程执行 / 是否有必要加锁;如果没有加锁,就会在编译过程自动把锁去掉;-》锁消除;

示例:
【注】synchronized不能滥用,见方法就加锁这是不适用的;
对于StringBuffer,它的关键方法都加synchronized关键字;如果是单线程使用,没有线程安全问题,编译器自动判断,没有必要加锁会自动去掉synchronized;

4.3 锁粒度
锁粒度:synchronized代码块中包含代码的多少;代码多,粒度大,代码少,粒度少;

多线程执行为了提高效率,尽量让串行的代码少,并发的代码多(并发代码越多,效率越高);
特殊情况:
某个场景需要频繁加锁 / 解锁,编译器优化操作为一个粗粒度锁;因为频繁的加锁 / 解锁都需要开销;

在这里插入图片描述

锁粗化开实际的情况进行粗化;


总结

✨✨✨各位读友,本篇分享到内容如果对你有帮助给个👍赞鼓励一下吧!!
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!

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

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

相关文章

QT桌面的构建

文章目录 前言一、按键类的创建二、按键样式设计三、详细代码讲解总结前言 本篇文章开始我将带大家构建一个属于自己的QT桌面,这个桌面将适用于ARM和Linux等。 一、按键类的创建 首先我们需要创建一个按键类,这个按键类需要包含一个按键和一个lable用来显示按键的名字。 效…

2022MathorCup赛题B

以下所有文字均基于作者的实际经验&#xff0c;并不具有完全的合理性&#xff0c;请谨慎参考 目录 一、问题分析 &#xff08;一&#xff09;问题一 &#xff08;二&#xff09;问题二 二、预处理 &#xff08;一&#xff09;训练集预处理 &#xff08;二&#xff09;测…

逻辑回归预测泰坦尼克号乘客生存率

逻辑回归预测泰坦尼克号乘客生存率 描述 RMS泰坦尼克号的沉没是历史上最臭名昭着的沉船之一。1912年4月15日&#xff0c;在她的处女航中&#xff0c;泰坦尼克号在与冰山相撞后沉没&#xff0c;在2224名乘客和机组人员中造成1502人死亡。这场耸人听闻的悲剧震惊了国际社会&…

Segment Anything文章要点

核心方法: 1. 数据 Unfortunately, there is no web-scale data source for segmentation; to address this, we build a “data engine” 没有大规模的数据源用于分割; 为了解决这个问题&#xff0c;我们构建了一个“数据引擎” We iterate between using our efficient mo…

SpringBoot整合MongoDB

参考链接 https://www.mongodb.org.cn/ 文章目录一、前言1.1 NoSQL介绍1.1.1 NoSQL 数据库分类1.1.2 NoSQL的优点/缺点1.1.3 BASE1.2 MongoDB介绍1.2.1 MongoDB和SQL对比1.2.2 数据库1.2.3 元数据1.2.4 MongoDB 数据类型二、SpringBoot整合MongDB2.1 环境配置2.2 MongoTemplate…

chatGPT如何助力devops

随着软件开发行业的不断发展&#xff0c;DevOps已成为软件开发的主流方法。DevOps是一种将开发和运维工作结合起来的实践&#xff0c;旨在提高软件交付速度和质量&#xff0c;以及优化整个软件开发过程。最近&#xff0c;聊天机器人技术的崛起为DevOps带来了全新的变革&#xf…

【MySQL】(1)数据库基础,库与表的增删查改,数据库的备份与还原

文章目录服务器&#xff0c;数据库&#xff0c;表关系MySQL 数据存储逻辑SQL 分类存储引擎库的操作查看数据库创建数据库查看创建语句删除数据库选择&#xff08;切换&#xff09;数据库查看当前选择的数据库修改数据库字符集和排序规则表的操作创建表查询表查询表结构插入数据…

2、Git使用不完全指南:Git客户端的使用及使用Token认证方式提交代码详解(详细图文)

本篇介绍如何在Windows、Linux下安装Git客户端以及从远程代码仓库拉取代码。以及介绍2023如何从GitHub上获取Token令牌、使用Pycharm通过Token令牌从本地push上传仓库的方法&#xff0c;用来解决推送代码报Push failed The requested URL returned error:403的错误问题。 安装…

通讯录的实现(初级版本)

思维导图&#xff1a; 目录 思维导图&#xff1a; ​编辑 一&#xff0c;实现通讯录的第一步——实现逻辑 二&#xff0c;头文件内实现的内容 三&#xff0c;contect.c文件内的函数定义 3.1初始化通讯录:void InitContect(cotect*pc) 3.2:实现通讯录内容的增加&#xff…

投稿指南【NO.14】SCI期刊推荐(mdpi旗下)

写在前面&#xff1a;SCI论文发表是我国高校机构与基地评估、成果评奖、项目立项、人才培养等方面的重要考核指标&#xff0c;更不要要说关系到我们个人的毕业、晋升和前途。SCI含金量之高&#xff0c;要是读研读博科研工作期间能发表&#xff0c;将对直博、找工作、国奖申请、…

04 | 连接池:别让连接池帮了倒忙

04 | 连接池&#xff1a;别让连接池帮了倒忙 连接池一般对外提供获得连接、归还连接的接口给客户端使用&#xff0c;并暴露最小空闲连接数、最大连接数等可配置参数&#xff0c;在内部则实现连接建立、连接心跳保持、连接管理、空闲连接回收、连接可用性检测等功能。 注意鉴…

MySQL 主从同步及延迟原因分析

主从同步的基本原理 MySQL主从同步步骤详见 MySQL binlog模式及主备的基本原理 谈到主备的并行复制能力&#xff0c;我们要关注的是图中黑色的两个箭头。一个箭头代表了客户端写入主库&#xff0c;另一箭头代表的是从库上sql_thread执行中转日志&#xff08;relay log&#xff…

《Squeeze-and-Excitation Networks》

Squeeze-and-Excitation Networks1. 摘要2. 介绍3. 网络设计4. 代码实现1. 摘要 卷积操作可以使网络通过在每层的局部感受野内融合空间和通道级信息以构建信息特征&#xff1b;空间信息尝试通过加强CNN整个特征层次的空间编码质量以加强CNN的表示能力&#xff1b;本文主要聚焦…

【计算机网络-应用层】万维网

文章目录1 统一资源定位符 URL2 万维网文档2.1 超文本标记语言 HTML2.2 层叠样式表 CSS2.3 JavaScript3 超文本传输协议 HTTP3.1 HTTP 报文格式3.1.1 HTTP 请求报文3.1.2 HTTP 响应报文3.2 HTTP 的工作过程3.2.1 HTTP/1.03.2.2 HTTP/1.13.2.3 相关例题4 Cookie1 统一资源定位符…

如何从根本上防止服务器被攻击

随着互联网的发展&#xff0c;服务器成为了企业和个人网络应用的重要基础设施。但是&#xff0c;随之而来的网络安全威胁也在不断增加&#xff0c;服务器安全问题也成为了影响企业信息安全的重要因素。针对这种情况&#xff0c;服务器安全防御变得尤为重要。   服务器安全防御…

6.Swagger的实战使用

六.Swagger的实战使用 1.什么是swagger 2.swagger的基本使用 3.swagger实战使用 六.Swagger的实战使用 1.什么是swagger swagger是后端接口文档的生成并且提供ui界面进行测试过去用postman测试 缺点&#xff1a;需要自己写地址&#xff0c;如果项目变了需要自己更改 2.sw…

CF区间DP作业题解

1. Recovering BST 由于互质关系不是传递的&#xff0c;所以尽量挂在树的最下面&#xff0c;刚好构成二叉树 f[i][j][0]f[i][j][0]f[i][j][0] 表示区间 [i,j][i,j][i,j] 以 iii 为根&#xff0c;是否可以构成一棵树。 f[i][j][1]f[i][j][1]f[i][j][1] 表示区间 [i,j][i,j][i,j…

Spring理论学习

1、什么是IOC IoC&#xff08;Inversion of Control:控制反转&#xff09; 是一种设计思想&#xff0c;而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权&#xff0c;交由 Spring 框架来管理。不过&#xff0c; IoC 并非 Spring 特有&#xff0c;…

阿里通义千问、ChatGPT和文心一言有何区别,在哪里能使用?

目前&#xff0c;聊天机器人技术在人工智能领域的发展越来越成熟了。现在已经有几款备受关注的聊天机器人产品问世&#xff0c;例如ChatGPT、阿里的通义千问和百度的文心一言。它们有什么区别&#xff0c;怎么使用呢&#xff1f; 其实&#xff0c;我也挺好奇的&#xff0c;毕竟…

人人拥有ChatGPT的时代来临了,这次微软很大方!

技术迭代的在一段时间内是均匀发展甚至止步不前的&#xff0c;但在某段时间内会指数级别的爆发。 ChatGPT背后的GPT 3.5训练据说花了几百万美金外加几个月的时间&#xff0c;参数大概有1700多亿。 这对于绝大多数的个人或企业来说绝对是太过昂贵的。 然而&#xff0c;微软&am…