常见的锁策略(面试八股文)

1.乐观锁vs悲观锁

        乐观锁:预测该场景中不太会出现锁冲突的情况。(后续做的工作会更少)

        悲观锁:预测该场景非常容易出现锁冲突(后续做的工作会更多)

        锁冲突:多个线程同时尝试去获得同一把锁,其中一个线程能够获取成功,其余线程阻塞等待

        乐观锁和悲观锁是在加锁之前,对锁冲突概率的预测,决定开销的多少

2.重量级锁vs轻量级锁

        重量级锁:加锁的开销是比较大的(花的时间多,占用的资源多),一个悲观锁很可能就是一个重量级锁(不绝对)

        轻量级锁:加锁的开销比较小(花的时间少,占用的资源少),一个乐观锁很可能就是一个轻量级锁(不绝对)

        .重量级和轻量级锁是在加锁之后对锁实践开销的考量

3.读写锁vs互斥锁

        互斥锁:就是普通的锁,没有为读操作和写操作分开加锁,synchronized就是互斥锁

        读写锁:把读操作加锁和写操作加锁分开了

                因为多线程同时去读同一个变量不会涉及到线程安全问题

                如果两个线程,一个线程读加锁,另一个线程也是读加锁,不会发生锁竞争(多线程并发执行的效率就会更高)

                如果两个线程,一个线程写加锁,另一个线程也是写加锁,会发生锁竞争

                如果两个线程,一个线程写加锁,另一个线程是读加锁,会发生锁竞争

        在实践开发中,读操作的频率往往比写操作的频率高,所以通过读写锁可以大大提高程序运行的效率(因为在多个线程对同一个变量进行读取操作的时候不会发生锁竞争,是并发执行的)

4.自旋锁vs挂起等待锁

        自旋锁:一种典型的轻量级锁的实现方式,线程在抢锁失败后会进入阻塞状态,要等到获得锁的线程释放锁才能去尝试获取锁,而自旋锁会在获取锁失败了以后,立即再尝试去获取锁,无限循环(一直不停的去尝试获取锁)这样一旦锁被其他线程释放,就能够第一时间获得锁。

                优点:一旦锁被释放可以第一时间获得锁(对比挂起等待锁,获得锁的速度会快很多)

                缺点:如果锁被其他线程持有的时间比较久,那么就会持续的消耗cpu资源(而挂起等待的时间是不消耗cpu的)

        挂起等待锁:是重量级锁的一种典型表现,当出现锁冲突的时候,会牵扯到内核对于线程的调度,使冲突的线程出现挂起(阻塞等待)

5.公平锁vs非公平锁

        假设三个线程A, B,C.A先尝试获取锁,获取成功.然后B再尝试获取锁,获取失败,阻塞等待;然后C也尝试获取锁,C也获取失败,也阻塞等待.
        当线程A释放锁的时候,会发生啥呢?
        公平锁:遵守"先来后到".B比C先来的.当A释放锁的之后,B就能先于C获取到锁

        非公平锁:不遵守"先来后到".B和C都有可能获取到锁.

6.可重入锁和不可重入锁

        有一个线程,针对同一个对象,连续加锁了两次,如果产生了死锁就是不可重入锁,如果没有产生死锁就是可重入锁

        例子:

public synchronized void increase(){synchronized(this){count++;}
}

        如上述代码,调用increase方法后会在同一个线程中对同一个对象进行两次加锁,如果是不可重入锁的话,在调用increase方法对类对象进行加锁了以后,执行increase方法中的程序再对类对象进行加锁就会发生阻塞等待,一直要阻塞到第一个对类对象加锁的线程释放锁,但要执行完该线程又需要获得锁了以后才能向下执行,所以程序就卡在了第二次对类对象加锁这里,也就形成了死锁。

死锁的三种经典情况

        1.一个线程一把锁,但是是不可重入锁,该线程针对这个锁连续加锁两次,就会出现死锁

        2.两个线程,两把锁,这两把线程先分别获取到一把锁,然后再同时尝试获取对方的那把锁

通过下面的代码可以直观的看到该情况

ublic class Demo1 {public static void main(String[] args) {Object blocer1=new Object();Object blocer2=new Object();Thread t1=new Thread(()->{synchronized(blocer1){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized(blocer2){System.out.println("t1线程获取两把锁");}}});Thread t2=new Thread(()->{synchronized(blocer2){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized(blocer1){System.out.println("t2线程获取两把锁");}}});t1.start();t2.start();}
}

        3.N个线程M把锁,N个线程中每个线程都会对多把锁进行加锁,而且加的锁是有重复的,这样在莫种特殊情况下就会形成死锁

产生死锁的四个必要条件

        1.互斥使用,一个线程获取到一把锁了以后,其他线程就不能去获取这把锁(实际使用的锁,一般都是互斥的,锁的基本特征)

        2.不可抢占,锁只能是持有者主动释放,而不是被其他其他线程直接抢走(也是锁的基本特征)

        3.请求和保持,当一个线程去尝试获取多把锁,在获取第二把锁的时候会保持对第一把锁的获取状态(取决于代码结构)

        4.循环等待,t1尝试获取locker2,需要t2执行完,释放locker2,t2尝试获取locker1,需要t1执行完,释放locker1,发生了死循环(取决于代码结构)

解决死锁的有效方法

        解决死锁主要是针对循环等待这方面来进行解决

        当每个线程要获取多把锁,先针对锁进行编号,约定,每个线程如果要获取多把锁,必须先获取编号小的锁,后获取编号大的锁。

        只要所有线程加锁的顺序,都严格遵守上述顺序,就一定不会出现循环等待,就解决了死锁

synchronized采用的锁策略

        1.即是悲观锁也是乐观锁,即是重量级锁也是轻量级锁,是自适应的

        2.重量级锁部分是根据系统的互斥锁实现的,轻量级锁部分是根据自旋锁实现的

        3.非公平锁(不会遵循先来后到,锁释放后,那个线程获得锁,各凭本事)

        4.可重入锁,同一个线程可以多次获取同一把锁(内部会记录哪个线程拿了锁,记录引用计数,要是是同一个线程拿锁,计数器就加一,此时该线程要解锁的话,计数器就减一,直到1计数器为0才真正解锁)

        5.不是读写锁,是普通的互斥锁

synchronized的自适应过程

        代码写了一个synchronized之后,这里可能会产生一系列的自适应过程:无锁->偏向锁->轻量级锁->重量级锁

        一开始是无锁的,加了synchronized后就对程序加了偏向锁,偏向锁,不是真的加锁,而只是做了一个标记,如果有别的线程来竞争锁了,才会真的加锁,如果没有别的线程竞争,就自始至终都不会真的加锁

        偏向锁真的加锁以后就是轻量级锁,但是后续如果竞争这把锁的线程越来越多了(锁冲突更激烈了),从轻量级锁升级成了重量级锁

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

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

相关文章

mysql进阶-用户的创建_修改_删除

1. 使用mysql单次查询 [rootVM-4-6-centos /]# mysql -h localhost -P 3306 -p mytest -e "select * from book1"; Enter password: ------------------------------------------- | id | category_id | book_name | num | ----------------------------…

第17节 R语言分析:生物统计数据集 R 编码分析和绘图

生物统计数据集 R 编码分析和绘图 生物统计学,用于对给定文件 data.csv 中的医疗数据应用 R 编码,该文件是患者人口统计数据集,包含有关来自各种祖先谱系的个体的标准信息。 数据集特征解释 脚本 output= file("Output.txt") # File name of output log sink(o…

opencv读取MP4文件和摄像头数据

文章目录 前言一、waitKey函数二、VideoCapture类总结前言 本篇文章来讲解opencv读取MP4文件和摄像头数据,opencv主要用于处理图像数据那么本篇文章就来讲解opencv读取MP4文件和摄像头数据。 一、waitKey函数 waitKey()函数是OpenCV中常用的一个函数,它用于等待用户按键输…

docker常用命令

docker常用命令 1.镜像与容器的关系2. 基本命令3. 容器操作4. 镜像操作5. 文件传输 1.镜像与容器的关系 镜像是类容器是对类实例化的对象 2. 基本命令 启动docker服务systemctl start docker重启docker服务systemctl restart docker 开机自动启动dockersystemctl enable doc…

Spring-mybatis结合的底层原理

1.项目前期准备 1.1 导入maven jar包 <dependencies><!-- spring依赖 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.2.5.RELEASE</version></depende…

js中的设计模式

设计模式 代码整体的结构会更加清楚&#xff0c;管理起来会更加方便&#xff0c;更好地维护 设计模式是一种思想 发布订阅 模块化开发 导入很多模块 容器即数组存储未来要执行的方法&#xff0c;同addEventListener 数组塌陷问题* 由于删除了元素&#xff0c;导致从删除元素的位…

数据库知识-事务

数据库知识-事务 当处理数据库中的多个操作时&#xff0c;事务是一个关键的概念&#xff0c;它确保这些操作要么全部成功执行&#xff0c;要么全部回滚到初始状态&#xff0c;以保持数据的一致性。在SQL中&#xff0c;事务具有四个关键属性&#xff08;ACID&#xff09;&#…

4.Makefile使用自动推导(附示例)

一、Makefile自动推导理解 在Makefile中,自动推导是通过一些内置规则(implicit rules)来实现的。这些内置规则定义了一些常见的编译和链接操作,用于根据文件的扩展名自动推导生成目标文件和可执行文件。 当make命令执行时,它会查找Makefile中的目标和依赖关系,并根据这…

easyui实用点

easyui实用点 1.下拉框&#xff08;input框只能选不能手动输入编辑&#xff09; data-options"editable:false"//不可编辑2.日期框&#xff0c;下拉框&#xff0c;文本框等class class"easyui-datebox"//不带时分秒 class"easyui-datetimebox"…

idea调节文字大小、日志颜色、git改动信息

idea调节菜单栏文字大小&#xff1a; 调节代码文字大小&#xff1a; 按住ctrl滚动滑轮可以调节代码文字大小&#xff1a; 单击文件即可在主窗口上打开显示&#xff1a; idea在控制台对不同级别的日志打印不同颜色 &#xff1a; “grep console”插件 点击某一行的时候&#x…

关于会议OA需求分析与开发功能设计

前言&#xff1a;现如今&#xff0c;企业在会议管理方面对OA系统的需求越来越高。因为会议是企业内部沟通和协作的重要环节&#xff0c;一个高效的会议管理系统可以帮助企业提升会议效率、降低成本&#xff0c;并且提高内部信息共享的效果。 目录 一&#xff0c;以下是OA系统在…

React从入门到实战-react脚手架,消息订阅与发布

创建项目并启动 全局安装 npm install -g create-react-app切换到想创建项目的目录&#xff0c;使用命令&#xff1a;create-react-app 项目名称 ​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存中…(iQ6hEUgAABpQAAAD1CAYAAABeIRZoAAAAAXNSR0IArs4c6QAAIABJREFUe…

[threejs]相机与坐标

搞清相机和坐标的关系在threejs初期很重要&#xff0c;否则有可能会出现写了代码&#xff0c;运行时一片漆黑的现象&#xff0c;这种情况就有可能是因为你相机没弄对。 先来看一下threejs中的坐标(世界坐标) 坐标轴好理解&#xff0c;大家只需要知道在three中不同颜色代表的轴…

ARM单片机中断处理过程解析

前言 中断&#xff0c;在单片机开发中再常见不过了。当然对于中断的原理和执行流程都了然于胸&#xff0c;那么对于ARM单片机中断的具体处理行为&#xff0c;你真的搞清楚了吗&#xff1f; 今天来简单聊一聊&#xff0c;ARM单片机中断处理过程中的具体行为是什么样的&#xf…

一张图像相当于 16×16 个单词:用于大规模图像识别的 Transformers(视觉 Transformers)

一张图片值多少字? 一张图片胜过千言万语?无法用言语完整地描述一幅图画。但论文告诉我们一张图像相当于 1616 个单词。在这篇博客中,我将解释使用 Transformer 进行图像识别。这是一篇非常有趣的论文, 这篇论文有什么特别之处? 它很特别,因为这里我们不会使用任何卷积网…

【Linux】配置java

yum -y install java-1.8.0-openjdk-devel.x86_64 可以直接使用 由于后续需要安装一些软件需要JAVA_HOME 所以需要配置JAVA_HOME 用yum下载的软件的目录是软连接的 which javals -lrt /usr/bin/javals -lrt /etc/alternatives/java /etc/profile 会自动加载/etc/profile.d…

vite+typescript项目 :找不到模块“./***.vue”或其相应的类型声明——解决方案

vue3ts报错&#xff1a; 找不到模块“./App.vue”或其相应的类型声明。ts(2307) 解决方法&#xff1a; 1、在src文件夹找到 vite-env.d.ts 加入以下代码&#xff1a; declare module *.vue {import type { DefineComponent } from vueconst vueComponent: DefineComponent<…

django使用ztree实现树状结构效果,子节点实现动态加载(l懒加载)

一、实现的效果 由于最近项目中需要实现树状结构的效果,考虑到ztree这个组件大家用的比较多,因此打算在django项目中集成ztree来实现树状的效果。最终实现的示例效果如下: 点击父节点,如果有子节点,则从后台动态请求数据,然后显示出子节点的数据。 二、实现思路 …

蓝桥杯上岸每日N题 第六期(求阶乘)!!!

蓝桥杯上岸每日N题第六期 ❗️ ❗️ ❗️ 同步收录 &#x1f447; 蓝桥杯上岸必背&#xff01;&#xff01;&#xff01;(持续更新中~) 大家好 我是寸铁&#x1f4aa; 冲刺蓝桥杯省一模板大全来啦 &#x1f525; 蓝桥杯4月8号就要开始了 &#x1f64f; 距离蓝桥杯省赛倒数…

AndroidBanner - ViewPager

解决banner 不可见依旧轮播的问题 思考一下&#xff1a;什么时候可以轮播&#xff0c;什么时候不可以轮播 当Banner添加到屏幕上&#xff0c;且对用户可见的时候&#xff0c;可以开始轮播 当Banner从屏幕上移除&#xff0c;或者Banner不可见的时候&#xff0c;可以停止轮播 当…