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


前言

本篇介绍什么是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代码块中包含代码的多少;代码多,粒度大,代码少,粒度少;

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

在这里插入图片描述

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


总结

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

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

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

相关文章

【无人机】基于灰狼优化算法的无人机路径规划问题研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

链式二叉树及相关操作(前,中,后,层序遍历)

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; “春来无事&#xff0c;只为花忙。” 前言: 上一期给大家介绍了二叉树的一种顺序结构&#xff1a;堆&#xff0c;这一期承接上一期&#xff0c;给大家继续介绍二叉树的另一种结构&#xff1a;链式结构。 目录…

golang指针相关

指针相关的部分实在是没有搞太明白&#xff0c;抽时间来总结下。 1.指针相关基础知识 比如现在有一句话&#xff1a;『谜底666』&#xff0c;这句话在程序中一启动&#xff0c;就要加载到内存中&#xff0c;假如内存地址0x123456&#xff0c;然后我们可以将这句话复制给变量A&…

多线程(八):常见锁策略

目录 前言 1. 乐观锁 VS 悲观锁 乐观锁 悲观锁 2. 轻量级锁 VS 重量级锁 轻量级锁 3. 自旋锁 VS 挂起等待锁 自旋锁 挂起等待锁 4. 读写锁 VS 互斥锁 5. 可重入锁 vs 不可重入锁 死锁 发生死锁的情况 死锁产生的四个必要条件如下&#xff1a; 6. 公平锁和非公平锁…

【Java EE】-多线程编程(九) 锁策略CAS锁优化

作者&#xff1a;学Java的冬瓜 博客主页&#xff1a;☀冬瓜的主页&#x1f319; 专栏&#xff1a;【JavaEE】 分享&#xff1a; 主要内容&#xff1a;乐观锁VS悲观锁、轻量级锁VS重量级锁、自旋锁VS挂起等待锁、互斥锁VS读写锁、公平锁VS非公平锁、可重入锁VS不可重入锁。CAS实…

Python数据结构与算法-树

一、树的概念详情见 https://blog.csdn.net/little_limin/article/details/129845592 Python数据结构与算法-堆排序&#xff08;NB组&#xff09;—— 一、树的基础知识二、树的实例&#xff1a;模拟文件系统1、树的存储树结构也是链式存储的&#xff0c;与链表的结构相似&…

类ChatGPT代码级解读:如何从零起步实现Transformer、llama/ChatGLM

前言 最近一直在做类ChatGPT项目的部署 微调&#xff0c;关注比较多的是两个&#xff1a;一个LLaMA&#xff0c;一个ChatGLM&#xff0c;会发现有不少模型是基于这两个模型去做微调的&#xff0c;说到微调&#xff0c;那具体怎么微调呢&#xff0c;因此又详细了解了一下微调代…

Vulnhub_Pylington

目录 一、信息收集 &#xff08;一&#xff09;端口服务探测 &#xff08;二&#xff09;目录扫描 二、漏洞挖掘 &#xff08;一&#xff09;robots敏感信息泄露 &#xff08;二&#xff09;python IDE沙箱绕过RCE 1. python敏感函数沙盒绕过 2. exec(__import_…

【ES】搜索结果处理RestClient查询文档

【ES】搜索结果处理&RestClient查询文档2.搜索结果处理2.1.排序2.1.1.普通字段排序2.1.2.地理坐标排序2.2.分页2.2.1.基本的分页2.2.2.深度分页问题2.2.3.小结2.3.高亮2.3.1.高亮原理2.3.2.实现高亮2.4.总结3.RestClient查询文档3.1.快速入门3.1.1.发起查询请求3.1.2.解析响…

Python做个猫狗识别系统,给人美心善的邻居

嗨害大家好鸭&#xff01;我是爱摸鱼的芝士❤ 宠物真的看着好治愈 谁不想有一只属于自己的乖乖宠物捏~ 这篇文章中我放弃了以往的model.fit()训练方法&#xff0c; 改用model.train_on_batch方法。 两种方法的比较&#xff1a; model.fit()&#xff1a;用起来十分简单&#…

Kubernetes 部署 StarRocks 集群

文章目录StarRocks简介系统架构图安装部署StarRocks手动部署通过 Docker部署使用 StarGo 部署管理通过 StarRocks Manager部署管理通过 Kubernetes部署工作原理逻辑图部署 StarRocks Operator部署 StarRocks 集群访问 StarRocks 集群集群内访问 StarRocks 集群集群外访问 StarR…

【案例实践】R语言多元数据统计分析在生态环境中的实践应用

查看原文>>>R语言生物群落分析绘图、多元统计分析、CMIP6、遥感碳储量、GEE林业、InVEST等 生态环境领域研究中常常面对众多的不同类型的数据或变量&#xff0c;当要同时分析多个因变量&#xff08;y&#xff09;时需要用到多元统计分析&#xff08;multivariate sta…

Spark----DataFrame和DataSet

Spark之DataFrame和DataSet 文章目录Spark之DataFrame和DataSetDataFrameDSL 语法创建DataFrame查看DataFrame的Schema信息只查看列数据的6种方式按照“age”分区&#xff0c;查看数据条数增加列withColumn修改列名withColumnRenamedRDD 转换为 DataFrameDataFrame 转换为 RDD转…

音质蓝牙耳机哪款好用?2023公认音质好的四款蓝牙耳机推荐

现如今&#xff0c;蓝牙耳机越来越受欢迎&#xff0c;不少人在听歌、追剧、甚至是玩游戏的时候都会戴着它。最近看到很多人问&#xff0c;音质蓝牙耳机哪款好用&#xff1f;针对这个问题&#xff0c;我来给大家推荐四款公认音质好的蓝牙耳机&#xff0c;一起来看看吧。 一、南…

算法笔记:Frechet距离度量

曲线之间相似性的度量&#xff0c;它考虑了沿曲线的点的位置和顺序 1 概念 1.1 直观理解 主人走路径A&#xff0c;狗走路径B&#xff0c;他们有不同的配速方案主人和狗各自走完这两条路径过程中所需要的最短狗绳长度 &#xff08;在某一种配速下需要的狗绳长度&#xff09;&a…

考研复试确认神操作!

终于进行到了研究生考试的尾声&#xff0c;但让考生感到无力吐槽的事情&#xff0c;却还在继续上演&#xff0c;比如苏科大&#xff0c;再比如中地大、苏大&#xff0c;三所学校的神操作&#xff0c;着实让无数考生忍不住调侃&#xff1a;原来考研不仅拼实力&#xff0c;还得拼…

你的APP内存还在暴增吗?试着用Bitmap管理下内存~

作者&#xff1a;layz4android 相信伙伴们在日常的开发中&#xff0c;一定对图片加载有所涉猎&#xff0c;而且对于图片加载现有的第三方库也很多&#xff0c;例如Glide、coil等&#xff0c;使用这些三方库我们好像就没有啥担忧的&#xff0c;他们内部的内存管理和缓存策略做的…

如何实现Chatgpt写文章(附chatgpt3.5免费接口)

申明&#xff1a;本次只是说一下实现思路&#xff0c;官方的接口以及如何实现方式&#xff0c;本文没有提及&#xff0c;这次只是一个思路&#xff0c;若想代替人工完成质量还差的很远&#xff0c;请审核大大放行 今天再次优化了代码&#xff0c;修复了一些bug&#xff0c;考虑…

VUE 学习笔记(一)开发环境搭建

1、Visual Studio Code安装及使用 下载地址官网&#xff1a;https://code.visualstudio.com/ 直接点击下载按钮即可&#xff0c;会根据系统自动下载合适的版本&#xff0c;无需自行选择。 2、VSCode 上安装&#xff1a;JavaScript Debugger 目前 Debugger for Chrome 已经处…

使用向量机(SVM)算法的推荐系统部署实现

包括3个模块&#xff1a;数据预处理、模型训练及保存、模型测试&#xff0c;下面分别给出各模块的功能介绍及相关代码。 数据集下载链接为https://www.aitechclub.com/data-detail? data_id29&#xff0c;停用词典下载链接为http://www.datasoldier.net/archives/636。 1.数…