常见锁策略之可重入锁VS不可重入锁

可重入锁VS不可重入锁

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

死锁

我们之前引入多线程的时候不是讲了一个加数字的案例么,我们今天以它来举例

当我们这样写的时候会出现什么问题?

分析:第一个synchronized的加锁对象是 this ,当我们继续执行代码,就会发现第二个synchronized的加锁对象还是 this ,此时就需要注意,当一个对象已经被加锁了,此时尝试对这个已经加锁的对象再一次的进行加锁,就会出现"锁竞争",我们要想使得第二个synchronized实现对 this 的加锁,就要让increase执行完毕,但是要想让increase执行完毕,就需要第二个synchronized加锁成功,此时就陷入了循环,就出现了矛盾.此时这个代码就卡在这里了,因此这个线程就僵住了.这是死锁的第一个体现形式.

这里的关键在于,两次加锁,都是"同一个线程",第二次尝试加锁的时候,该线程已经有了这个锁的权限了,这个时候,不应该加锁失败的,不应该阻塞等待的.

如果是一个不可重入锁,这把锁不会保存,是哪个线程对它加的锁,只要它当前处于加锁状态之后,收到了"加锁"这样的请求,就会拒绝当前加锁,而不管当下的线程是哪个,就会产生死锁.

如果是一个可重入锁,则是会让这个锁保存,是哪个线程加上的锁,后续收到加锁请求之后,就会先对比一下,看看加锁的线程是不是当前自己持有这把锁的线程,这个时候就可以灵活判定了.

但庆幸的是,synchronized本身就是一个可重入锁,实际上我们上述的那个举例子的代码并不会出现死锁的情况.

首先,答案是肯定的,不能释放,如果在这里释放锁了,那么中间的synchronized以及中间的代码就不会受到锁的保护了.

那么我们想一下,可重入锁,需要比不可重入锁额外多出哪些功能

1.判断当前加锁的线程是不是同一个线程

2.判断当前代码执行到的是第几层的锁,那么这个功能是怎么实现的呢?

其实很简单,持有一个"计数器"就可以了,让锁对象不光要记录是哪个线程持有锁,同时再通过一个整形变量记录当前这个线程加了几次锁,每遇到一个加锁操作,就计数器+1,每遇到一个解锁操作就-1,当计数器减为0的时候,才真正执行释放锁的操作,其他时候不释放锁.而类似于这个操作的操作,我们称它为"引用计数"

死锁的三种典型情况

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

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

下面我们敲代码来理解一下死锁

首先,这是一个错误的代码

执行结果如下

实际上由刚才的分析可以知道,这里会出现死锁的情况,那么为什么这里和理论值不一样呢?

因为没有加Sleep

代码如上

执行结果如下

那么,为什么?为什么加了Sleep之后会不一样,究竟是Sleep改变了线程原有的样子,还是说Sleep恢复了线程原有的样子?

答案是后者

我们在使用多线程的时候会发现,程序运行的时间特别长了会经常出现一些问题,或者说当我们来气了多个线程他们分别执行几个任务,但是因为执行的任务的时间非常短,有时候CPU切换的时候会出现一系列的问题.

原因是当我们设置Sleep是,就等于告诉CPU,当前的线程不再运行,持有当前对象的锁.那么这个时候CPU就会切换到另外的线程了,这种操作在有些时候是非常好的.

3.N个线程M把锁

哲学家就餐问题

每个哲学家,主要做两件事

1.思考人生,会放下筷子

2.吃面.会拿起左手和右手的筷子

3.每个哲学家,什么时候思考人生,什么时候吃面条,都不好说

2.每个哲学家一旦想吃面条了,就会非常固执的完成吃面条的这个操作,如果此时,他的筷子被别人使用了,就会阻塞等待,而且等待的过程中不会放下手中已经拿着的的筷子

是否有办法去避免死锁呢?

先明确产生死锁的原因,死锁的必要条件

四个必要条件(缺一不可,只要破坏其中任意一个条件,就可以避免死锁)

1.互斥使用,一个线程获取到一把锁之后,别的线程不能获取到这个锁(我们实际使用的锁,一般都是互斥的(锁的基本特性))

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

3.请求和保持,这一个线程尝试去获取多把锁,在获取第二把锁的过程中,会保持对第一把锁的获取状态(取决于代码结构)(很可能会影响到需求)

在获取第二把锁的同时会保持对第一把锁的状态,这里由于获取第二把锁的时候,并没有去释放第一把锁,所以就会出现阻塞等待

当我们将代码改成这样,即获取完第一把锁之后,并且将第一把锁释放掉,此时再去请求获取第二把锁,这样做是不会出现死锁的

4.循环等待.t1尝试获取 locker2,需要t2执行完,释放locker2;t2尝试获取locker1,需要t1执行完,释放locker1 (取决于代码结构)(解决死锁问题的最关键要点)

如果具体解决死锁问题,实际的方法有很多种(例:银行家算法(但不推荐,因为不接地气))

介绍一个更简单,也非常有效的解决死锁的办法

针对锁进行编号,并且规定加锁的顺序

比如,约定,每个线程如果要获取多把锁,必须先获取编号小的锁,后获取编号大的锁.只要所有线程加锁的顺序,都严格遵守上述顺序,就一定不会出现循环等待.

像这样,我们规定先让locker1加锁,然后让locker2加锁,按照指定顺序加锁,也就可以避免死锁的问题了

synchronized具体是采用了哪些锁策略

1.synchronized即是悲观锁,也是乐观锁.

2.synchronized即是重量级锁,也是轻量级锁.

3.synchronized重量级锁部分是基于系统的互斥锁实现的,轻量级锁部分是基于自旋锁实现的.

4.synchronized是非公平锁(不会遵守先来后到,锁释放了之后,哪个线程拿到锁,各凭本事).

5.synchronized是可重入锁(内部会记录哪个线程拿到了锁,记录引用次数).

6.synchronized不是读写锁.

synchronized内部实现策略(内部原理)

代码中写了一个synchronized之后,这里可能会产生一系列的"自适应的过程",锁升级(锁膨胀)

无锁->偏向锁->轻量级锁->重量级锁

偏向锁(懒汉模式思想的延伸)

不是真的加锁,而只是做了一个"标记".如果有别的线程来竞争锁了,才会真的加锁,如果没有别的线程竞争,就自始至终都不会真的加锁了.(加锁本身,有一定的开销,能不加就不加,非得是有人来竞争了,才会真的加锁)

偏向锁在没有其他人竞争的时候,就仅仅是一个简单的标记(非常轻量).一旦有别的线程尝试加锁,就会立刻把偏向锁升级为一个真正的加锁状态,让其他线程只能阻塞等待

轻量级锁

synchronized通过自旋的方式来实现轻量级锁,我这边把锁占据了,另一个线程就会按照自旋的方式,来反复查询当前的锁的状态是不是被释放了.但是,后续如果竞争这把锁的线程越来越多了(锁冲突更激烈了),就会从轻量级锁,升级成重量级锁

锁消除

编译器,会智能的判定,当前的这个代码,是否有必要加锁,如果你写了加锁,但实际上没有必要加锁,就会把加锁操作自动优化掉。

比如在单个线程中使用StringBuffer.

编译器进行优化,是要保证优化之后的逻辑和之前的逻辑是一致的

锁粗化

关于"锁的粒度"如果加锁操作里包含的实际要执行的代码越多,就认为锁的粒度越大.

//以下是一些伪代码
//锁的粒度小
for(.....){synchronized(this){count++;    }
}
//锁的粒度大
synchronized(this){for(.....){count++;    }
}

 

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

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

相关文章

前端基础--Vue3

Vue3基础 VUE3和VUE2的区别 2020年9月18日,Vue.js发布版3.0版本,代号:One Piece 于 2022 年 2 月 7 日星期一成为新的默认版本! Vue3性能更高,初次渲染快55%, 更新渲染快133% 。体积更小 Vue3.0 打包大小减少41%。 同时Vue3可以更好的支持T…

基于微服务智能推荐健康生活交流平台的设计与实现(SpringCloud SpringBoot)+文档

💗博主介绍💗:✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示:文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

解决使用monaco-editor编译器,编译器展示内容没有超过编译器高度,但是出现滚动条问题

前言: 最近在完成项目时,有使用编译器进行在线编辑的功能,就选用了monaco-editor编译器,但是实现功能之后,发现即使在编译器展示的内容没有超过编译器高度的情况下,编译器依旧存在滚动条,会展示…

计算机网络--网络层

一、网络层的服务和功能 网络层主要为应用层提供端对端的数据传输服务 网络层接受运输层的报文段,添加自己的首部,形成网络层分组。分组是网络层的传输单元。网络层分组在各个站点的网络层之间传输,最终到达接收方的网络层。接收方网络层将运…

如何在 Java 应用中使用 Jedis 客户端库来实现 Redis 缓存的基本操作

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…

堆与栈的概念(RTOS)

目录 #堆在RTOS的概念 #相关代码表示 #堆相关特点 #栈在RTOS中的概念 #栈的代码表示 #栈的相关特点 #为什么每个RTOS任务都要有自己的栈 前言:本篇参考韦东山老师的RTOS,连接放在最后 #堆在RTOS的概念 本文所指的堆与栈并不是数据结构中&#xff…

【unity实战】在Unity中使用有限状态机制作一个敌人AI

最终效果 文章目录 最终效果前言有限状态机的主要作用和意义素材下载逻辑图敌人动画配置优雅的代码文件目录状态机代码定义敌人不同状态切换创建敌人效果更多的敌人参考源码完结 前言 有限状态机以前的我嗤之以鼻,现在的我逐帧分析。其实之前我就了解过有限状态机&…

2.(vue3.x+vite)调用iframe的方法(vue编码)

1、效果预览 2.编写代码 (1)主页面 <template><div><button @click="sendMessage">调用iframe,并发送信息

【udp报文】udp报文未自动分片,报文过长被拦截问题定位

问题现象 某局点出现一个奇怪的现象&#xff0c;客户端给服务端发送消息&#xff0c;服务端仅能收到小部分消息&#xff0c;大部分消息从客户端发出后&#xff0c;服务端都未收到。 问题定位 初步分析 根据现象初步分析&#xff0c;有可能是网络原因导致消息到服务端不可达&a…

【C语言】文件的顺序读写

©作者:末央&#xff06; ©系列:C语言初阶(适合小白入门) ©说明:以凡人之笔墨&#xff0c;书写未来之大梦 目录 前言字符输入输出函数 - fgetc和fputc文本行输入输出函数 - fgets和fputs格式化输入输出函数 - fscanf和fprintf 前言 对文件数据的读写可以分为顺序…

Seal^_^【送书活动第8期】——《ChatGLM3大模型本地化部署、应用开发与微调》

Seal^_^【送书活动第8期】——《ChatGLM3大模型本地化部署、应用开发与微调》 一、参与方式二、本期推荐图书2.1 作者建语2.2 编辑推建2.3 图书简介2.4 前 言2.5 目 录 三、正版购买 大模型领域 既是繁星点点的未知宇宙&#xff0c;也是蕴含无数可能的广阔天地&#xff0c; 正…

idea创建自定义的maven spark scala archetype脚手架

一&#xff1a;先创建一个Maven项目net.alchim31.maven&#xff08;选该模板&#xff0c;得要等一会儿才能加载出来&#xff09; 之后将自己的目录结构建立好&#xff0c;最好不要有空目录&#xff0c;可能会因为没有文件在install的时候编译不进去 pom中内容也按照自己的需要改…

Stable Diffusion web UI 插件

2024.7.3更新&#xff0c;持续更新中 如果需要在linux上自己安装sd&#xff0c;参考&#xff1a;stable diffusion linux安装 插件复制到 /stable-diffusion-webui/extensions 目录下&#xff0c;然后重新启动sd即可 一、插件安装方法 每种插件的安装方法可能略有不同&#xf…

苹果p12证书最简单最新申请流程

使用uniapp打包&#xff0c;在ios上打正式包需要苹果的p12证书和证书profile文件&#xff0c;点进去uniapp的ios证书申请教程&#xff0c;通篇就是使用mac电脑申请的教程&#xff0c;假如没有mac电脑就无法继续了。 因此&#xff0c;假如没有mac电脑的同志们&#xff0c;可以参…

Pytest+Allure+Yaml+PyMsql+Jenkins+Gitlab接口自动化(五)Jenkins配置

一、背景 Jenkins&#xff08;本地宿主机搭建&#xff09; 拉取GitLab(服务器)代码到在Jenkins工作空间本地运行并生成Allure测试报告 二、框架改动点 框架主运行程序需要先注释掉运行代码&#xff08;可不改&#xff0c;如果运行报allure找不到就直接注释掉&#xff09; …

鸿蒙应用开发-时间屏幕

点击下载源码&#xff1a; https://download.csdn.net/download/liuhaikang/89509449 做一个时间屏幕&#xff0c;可以点击切换白色和黑色&#xff0c;有渐变效果&#xff0c;使用到了鸿蒙的动画效果。 在这个设计中&#xff0c;我们首先引入了通用能力包&#xff0c;以实现功…

Kubernetes 离线安装的坑我采了

Kubernetes 离线安装的坑我采了 一、Error from server: Get "https://xx.xx.xx.xx:10250/containerLogs/kube-system/calico-node-8dnvs/calico-node": tls: failed to verify certificate: x509: certificate signed by unknown authority二、calico 或 pod 启动正…

cesium公交车轨迹漫游

个人博客&#xff1a;CSDN 博客-满分观察网友 z 演示地址&#xff1a;哔哩哔哩-满分观察网友 z 这是一个用 Cesium.js 做的公交车轨迹漫游&#xff0c;实现的功能有加载站点和道路轨迹点数据、监听车辆的实时位置、车辆控制器。滚动屏等等。 文章目录 1. 地图初始化2. 数据渲…

【高中数学/基本不等式】已知:x,y均为正实数,且xy+2x+y=4 求:x+y的最小值?

【问题】 已知&#xff1a;x,y均为正实数&#xff0c;且xy2xy4 求&#xff1a;xy的最小值&#xff1f; 【来源】 https://www.ixigua.com/7147585275823292942?logTagf25494de7fce23a3a3d0 【解答】 解&#xff1a; 由xy2xy4 两边加二得 xy2xy24 2 分解因式得 (x1)(…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第53课-语音指令跳舞

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第53课-语音指令跳舞 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&…