【JavaEE初阶】深入理解不同锁的意义,synchronized的加锁过程理解以及CAS的原子性实现(面试经典题);

前言

🌟🌟本期讲解关于锁的相关知识了解,这里涉及到高频面试题哦~~~

🌈上期博客在这里:【JavaEE初阶】深入理解线程池的概念以及Java标准库提供的方法参数分析-CSDN博客

🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客

 

目录

📚️1.引言

📚️2.锁的策略

2.1乐观锁与悲观锁

2.2轻量级锁和重量级锁

2.3自旋锁和挂起等待锁

2.4普通互斥锁和读写锁

2.5公平锁和非公平锁

2.6可重入锁和不可重入锁

📚️3.synchronized的加锁过程

3.1锁升级

1.偏向锁阶段

2.轻量级锁

3.重量级锁

3.2锁消除

3.3锁粗化

📚️4.CAS的实现原理

4.1CAS的内部逻辑

4.2CAS实现线程安全

4.3CAS的原子性

📚️5.总结

📚️1.引言

    Hello!uu们小编又来啦,上期在介绍过线程池的理解后,相信大家已经对其有了更深的了解,致此多线程初阶已经完结,前面的博客也可以供大家学习,复习哟~~~

本期只要是讲解关于不同锁的不同的意义理解,例如:乐观锁和悲观锁,以及synchronized的加锁之前的操作.......那就直接开始吧!!!

📚️2.锁的策略

2.1乐观锁与悲观锁

这是锁的两种不同实现方式;

乐观锁:即加锁之前,预估在程序中的锁的冲突不大,因此在加锁的时候就不会进行太多的操作;

悲观锁: 即加锁之前,预估在程序中的锁的冲突很大,因此在加锁的时候就不会进行比较多的操作;

乐观锁的影响: 由于加锁的工作很少,所以加锁的时候就很快,但是缺点就是会造成更多的CPU资源的消耗(引入一些其他问题)

悲观锁的影响:由于加锁的时候工作比较多,所以在加锁的时候就比较满,所以此时造成的问题就很少;

2.2轻量级锁和重量级锁

轻量级锁:消耗的CPU资源多,加锁比较快=>这里理解为乐观锁;

重量级锁:消耗的CPU资源少,加锁比较慢=>这里理解为悲观锁;

这里的轻量级锁和重量级锁是加锁后对锁的一种评价,而乐观锁和悲观锁是加锁前的一种预估,这里是从两种不同的角度来描述一件事情;

2.3自旋锁和挂起等待锁

自旋锁:是轻量级锁的一种典型实现,一般搭配while循环,在加锁成功后退出循环,但是加锁不成功后,会进入循环再次尝试加锁;

挂起等待锁:是重量级锁的一种典型实现,在加锁失败后,会进入阻塞状态,直到获取到锁

自旋锁的使用:一般用于锁冲突比较小的情况,由于高速反复的尝试加锁,导致CPU的资源消耗上升,取而代之的是加锁的速度快,但是在线程多的情况下会发生“线程饿死”的问题

挂起等待锁的使用:一般用于所冲突比较大的情况,由于进入阻塞后,就是内核随机调度来进行执行,要进行的操作增加,导致加锁更慢了

synchronized锁:这里的synchronized锁具有自适应的能力的,例如在锁冲突情况比较严重的时候,这里的synchronized就是悲观锁、重量级锁、挂起等待锁.....所以这里的synchronized是根据当时的锁的冲突情况来进行自适应的~~~

2.4普通互斥锁和读写锁

普通互斥锁:即synchronized类似,只有加锁和解锁

读写锁:这里的解锁是一样的,但是在加锁的时候分为两种即“加读锁”与“加写锁”

这里的情况就是:

读锁和读锁这之间,不会出现锁的冲突

读锁和写锁这之间,会出现锁的冲突

写锁和写锁这之间,会出现锁的冲突

即一个线程加读锁的时候,另一个线程是可以“读”的,但是是不可以“写”的;

即一个现场加写锁的时候,另一个线程是不可以进行“读”和“写”的 

为什么要引入读写锁:

在线程的读的操作中,读这个操作本来就是线程安全的,但是使用synchronized任然要给这一部分要加锁,由于加锁这个操作本来就是一个低效率的操作;在读的过程中不加锁可以打打提升效率;

但是读的时候完全不加锁,可能会在读的时候进行写操作,所以这里又要加“读锁”;

2.5公平锁和非公平锁

公平锁:即等待加锁的时间越久,就应该在锁释放的时候,先加上锁(先来先到原则)

非公平死锁:即在锁释放后,获取锁的概率是一样的,存在竞争;

如下图所示:

2.6可重入锁和不可重入锁

可重入锁:即在加锁过后任然在这个线程继续进行加锁;

不可重入锁:即在加锁过后,这个线程就不能进行加锁了;

例如synchronized是一个可重入锁,但是在系统中的锁是一个不可重入锁;

可重入锁需要记录加锁的对象,以及加锁的次数;

📚️3.synchronized的加锁过程

在synchronized加锁之前会经历一下升级过程

3.1锁升级

1.偏向锁阶段

这里的偏向锁阶段的实现和之前讲解的“懒汉模式”是有一定的联系的,即非必要不加锁,但是这里的偏向锁,并不是真正意义上的加锁;

偏向锁:即一种非必要不加锁的模式,真正意义上是不加锁的,而是进行一次轻量级的标记

这里就是当没有锁进行竞争的话就会不加锁,只是轻量化标记一下,当有锁的竞争,那么这个标记的就会很快拿到锁;

偏向锁的作用: 存在锁冲突的情况下,这中锁没有提高效率,但是当没有锁的竞争后,因为只是轻量化标记,而不加锁,那么这里的效率就会得到很大的提升;

2.轻量级锁

轻量级锁:即通过自旋的方式进行实现,反复快速的进行加锁的操作

优点:在锁的释放后,能够快速的拿到并加上锁;

缺点:非常消耗CPU的资源;

这里synchronized会根据有多少个线程在参与竞争,如果比较多,那么就会升级成重量级锁;

3.重量级锁

重量级锁:即拿不到锁的线程不会进入自旋状态,而是进入阻塞状态,释放CPU资源;

最后由内核进行随机调度,从而加上锁;

3.2锁消除

锁消除:即synchronized的一种优化策略,但是比较保守;

即编译器在编译的时候优化一下两种情况:

1.不存在锁竞争,只有一个线程,那么此时就会进行锁消除

2.加锁的代码中没有涉及到成员变量的改动,只有局部变量的改动,就不需要进行加锁

3.3锁粗化

锁粗化:即讲一个细粒度的锁,转化为一个粗粒度的锁;

粒度:即在synchronized{ },这个括号里的代码越少,即粒度越细;代码越多,即粒度越粗

所谓的粒度粗化,如下图所示:

可以发现,这里频繁的加锁解锁会造成额外的时间开销,而直接一步到位可以剩下这部分的时间开销;

📚️4.CAS的实现原理

4.1CAS的内部逻辑

所谓的CAS即compare and swap,即一种比较和交换,这是一个特殊的CPU的指令

其内部伪代码:

boolean CAS(address,expectValue,swapValue){if(&address==expectValue){&address=swapValue;return true;}return false
}

 注意:这里是一段伪代码,不能进行运行,只是描述逻辑的,即先进行判断寄存器的值和地址值是否相等,相等就将另一个swap寄存器的值给地址值(内存地址值)

4.2CAS实现线程安全

CAS是CPU的一种指令,操作系统又对这个指令进行了封装,我们的Java又对操作系统的API进行了封装,那么我们就可以进行使用啦;

在之前我们在实现两个线程对count实现加法操作,需要进行加锁,但是有了CAS就可以不用进行加锁了;

代码如下:

public static AtomicInteger count=new AtomicInteger();public static void main(String[] args) throws InterruptedException {Thread t1=new Thread(()->{for (int i = 0; i <50000 ; i++) {count.getAndIncrement();}});Thread t2=new Thread(()->{for (int i = 0; i <50000 ; i++) {count.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println("最终count的值为:"+count.get());}

这里就是通过使用原子类工具,实现了没有加锁的仍然线程安全的代码;

注意:之前的count++是三个指令,在线程的随机调度中存在不同指令的穿插的情况,导致线程安全问题,但是getandincrement本来就是一个线程安全的指令(就是一个指令),天然就具有原子性;

4.3CAS的原子性

在实现原子类的伪代码如下图所示

即起初内存中的值为value,oldvalue是寄存器中的值,进入循环,当比较成功,那么就value的值就为value+1了;

当存在随机调度的时候:

 

那么此时就会有以下操作:

第一步:执行右边线程的操作

第二步:进行随机调度走的代码

 

注意:在次比较发现内存和寄存器的值是不一样的了,此时就会进行再次读取内存,在次进行循环比较,发现一样了,就会加1跳出循环

代价:这里的代价,就是while循环造成的自旋,CPU的消耗;

📚️5.总结

💬💬本期小编讲解了关于不同锁的基本概念,包括我们经常使用synchronized的加锁过程包含的“锁升级,锁消除,锁粗化”的一系列的操作,以及CAS的实现和我们之前线程安全的代码的举例,本篇主要是涉及到(关于锁面试题)~~~

🌅🌅🌅~~~~最后希望与诸君共勉,共同进步!!!

 


💪💪💪以上就是本期内容了, 感兴趣的话,就关注小编吧。

                             😊😊  期待你的关注~~~

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

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

相关文章

SpringBoot日常:redission的接入使用和源码解析

文章目录 一、简介二、集成redissionpom文件redission 配置文件application.yml文件启动类 三、JAVA 操作案例字符串操作哈希操作列表操作集合操作有序集合操作布隆过滤器操作分布式锁操作 四、源码解析 一、简介 Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格…

Windows Ubuntu下搭建深度学习Pytorch训练框架与转换环境TensorRT

Windows Ubuntu下搭建深度学习Pytorch训练框架与转换环境TensorRT JetBrains2024&#xff08;IntelliJ IDEA、PhpStorm、RubyMine、Rider……&#xff09;安装包Anaconda Miniconda安装.condarc 文件配置镜像源查看conda的配置和源(channel)自定义conda虚拟环境路径conda常用命…

破解反编译:使用 ClassFinal 保护你的SpringBoot代码

在当今数字化时代&#xff0c;保护源代码的安全性变得愈发重要。无论是企业的核心算法还是独特的业务逻辑&#xff0c;代码一旦暴露&#xff0c;便可能导致竞争优势的丧失和商业机密的泄露。因此&#xff0c;在使用 Java 和 Spring Boot 开发项目时&#xff0c;理解从源代码到可…

websocket连接异常报错1006

目录&#xff1a; 1、问题现象2、问题原因3、解决方案 1、问题现象 WebSocket状态码的作用&#xff1a; 在WebSocket协议中&#xff0c;状态码用于表示连接状态和错误信息。通过状态码&#xff0c;我们可以快速判断连接是否成功&#xff0c;以及出现错误时的原因。常见的WebSo…

教培机构如何向知识付费转型

在数字化时代&#xff0c;知识付费已成为一股不可忽视的潮流。面对这一趋势&#xff0c;教育培训机构必须积极应对&#xff0c;实现向知识付费的转型&#xff0c;以在新的市场环境中立足。 一、教培机构应明确自身的知识定位。 在知识付费领域&#xff0c;专业性和独特性是关键…

VUE前后端分离毕业设计题目项目有哪些,VUE程序开发常见毕业论文设计推荐

目录 0 为什么选择Vue.js 1 Vue.js 的主要特点 2 前后端分离毕业设计项目推荐 3 后端推荐 4 总结 0 为什么选择Vue.js 使用Vue.js开发计算机毕业设计是一个很好的选择&#xff0c;因为它不仅具有现代前端框架的所有优点&#xff0c;还能让你专注于构建高性能、高可用性的W…

Matlab实现白鲸优化算法优化回声状态网络模型 (BWO-ESN)(附源码)

目录 1.内容介绍 2部分代码 3.实验结果 4.内容获取 1内容介绍 2部分代码 %% 清空环境变量 warning off % 关闭报警信息 close all % 关闭开启的图窗 clear % 清空变量 clc % 清空命令行 tic load bwand %%…

CC2530定时器1中断实现定时1-3

源码 #include "iocc2530.h"//引用CC2530头文件int t1_Count0; //定时器1溢出次数计数void Init_Led(void){ /*******************LED1初始化部分******************/P1SEL &~ 0x01; //设置P1_0口为通用I/O口P1DIR | 0x01; //设置P1_0口为输出口P…

软考越来越难了,2024年软考究竟还值不值得考?

最近不少同学沟通&#xff0c;聊到软考现在越来越难了&#xff0c;考了两三次都没过&#xff0c;也有不少新同学咨询软考考试的一些福利政策&#xff0c;投入大量的物力&#xff0c;财力&#xff0c;精力&#xff0c;那么到底软考值不值得考呢&#xff1f; 01 / 关于软考 软考…

Leetcode 10. 正则表达式匹配

1.题目基本信息 1.1.题目描述 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。 ‘.’ 匹配任意单个字符‘*’ 匹配零个或多个前面的那一个元素 所谓匹配&#xff0c;是要涵盖 整个 字符串 s 的&#xff0c;而不是部分…

k8s的控制节点不能访问node节点容器的ip地址

master控制node服务器添加容器后,访问不了该node服务器容器的ip,只能在node服务器访问 排查后发现是k8s的master服务器和node节点的网址网段和k8s初始化时提示的ip网段不一致 我之前是192.168.137.50, 实际上master主机期望的是192.168.1.50 解决方案: 1.删除服务器后重建ma…

python爬虫 - 进阶requests模块

&#x1f308;个人主页&#xff1a;https://blog.csdn.net/2401_86688088?typeblog &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、SSL证书问题 &#xff08;一&#xff09;跳过 SSL 证书验证 &#xff0…

Vue3中提到的Tree-shaking

我们知道&#xff0c;Vue3中提到一个叫Tree-shaking的东西&#xff0c;其实也并不是一个新的东西&#xff0c;有人称之为"摇树优化"&#xff0c;什么意思&#xff1f; 按照作者的原话解释&#xff0c;Tree-shaking其实就是&#xff1a;把无用的模块进行“剪枝”&…

【Linux】进程间通信——System V消息队列和信号量

一、消息队列 1.1 概念 进程间通信的原理是让不同进程看到同一份资源&#xff0c;资源种类的不同就决定了通信方式的差异。如果用管道通信&#xff0c;则资源是文件缓冲区&#xff1b;如果用共享内存&#xff0c;则资源是内存块 消息队列是由操作系统提供的资源&#xff0c;…

postman自动化实战总结

Postman实战总结 简介 本次实战内容主要包括如下几点&#xff1a; l 背景介绍 l Postman使用&#xff0c;侧重于自动化实现&#xff0c;基础使用不做介绍 l 可视化Newman介绍 l 框架特色 l 实战中的坑 背景 随着国内软件技术的高速发展&#xff0c;越来越多的手工测试…

解决谷歌浏览器在安卓手机上的常见问题

在使用安卓手机浏览网页时&#xff0c;谷歌浏览器无疑是许多用户的首选。然而&#xff0c;在使用过程中&#xff0c;用户可能会遇到一些常见问题&#xff0c;如搜索图片困难、缓存积累过多导致浏览器卡顿&#xff0c;以及无法下载视频等。本文将针对这些问题&#xff0c;提供详…

【Linux】详解Linux下的工具(内含yum指令和vim指令)

文章目录 前言1. Linux下软件安装的方式2. yum2.1 软件下载的小知识2.2 在自己的Linux系统下验证yum源的存在2.3 利用yum指令下载软件2.4 拓展yum源&#xff08;针对于虚拟机用户&#xff09; 3. vim编辑器3.1 vim是什么&#xff1f;3.2 如何打开vim3.2 vim各模式下的讲解3.2.1…

【C语言】猜数字小游戏

&#x1f602;个人主页: 起名字真南 &#x1f923;个人专栏:【数据结构初阶】 【C语言】 【C】 目录 1 随机数的生成1.1 rand1.2 srand1.3 time1.4 设置随机数范围 2 猜数字游戏实现 前言&#xff1a;我们学习完前面的循环以后可以写一个猜数字小游戏 1 随机数的生成 想要完成…

新生培训 day1 C语言基础 顺序 分支 循环 数组 字符串 函数

比赛地址 b牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ C语言数据类型 字符 整型数 int 2e9 long long 9e18 浮点数 代码示例 /** Author: Dduo * Date: 2024-10-8* Description: 新生培训day1 */ #include <stdio.h>int main() {// 定义变量in…

QT-空窗口主窗口对话框

1. QMainWindow QMainWindow 用来创建主窗口 主窗口包含&#xff1a; 标题栏&#xff08;Window title&#xff09;、菜单栏&#xff08;MenuBar&#xff09;、工具栏&#xff08;ToolBar&#xff09;、状态栏&#xff08;StatusBar&#xff09;、停靠部件&#xff08;DockWid…