关于synchronized死锁问题

news/2024/10/31 16:51:37/

大家先猜一下下面这个代码是否可以成功运行?

java">Thread t = new Thread(() - >{ 
synchronized(locker){
synchronized(locker){
//..随便写点啥都行
System.out.println("hello");}}
});
t.start();

从直观上感觉,这个加锁应该是不能成功呀!!  此时locker对象处于已经加锁的状态~~

这个时候,如果你要是再尝试对 locker 加锁 不就会出现“阻塞” 情况吗~~

为啥最终没出现阻塞呢?? 

最关键问题,在于说,这两次加锁,其实是在同一个线程上进行的!!

当前由于是同一个线程,此时锁对象,就知道了第二次加锁的线程,就是持有锁的线程,第二次操作,就可以直接放行通过不会出现阻塞~~    这个特性,称为“可重入”

上述代码中真正加锁,同时把计数器 + 1(初始情况是0,+1之后变成1了,说明当前这个对象被该线程加锁异常)同时记录线程是谁~~

对于可重锁来说,内部会持有两个信息

1.当前这个锁是被哪个线程持有的~~

2.加锁次数的计数器

即使上述synchronized 嵌套个10层8层,也不会使解锁操作混乱,始终能够保持在正确的时机解锁

“死锁”

死锁是多线程代码中的一类经典问题~~

加锁是能够解决线程安全问题,但如果加锁方式不当,就可能产生“死锁”!!

1.死锁的三种典型场景~~

1.一个线程,一把锁。

刚才说情况,如果锁是不可重入锁,并且一个线程对这把锁加两次就会出现死锁~~

2.两个线程,两把锁。

线程1获取到锁A,线程2获取到锁B,接下来,1尝试获取锁B,2尝试获取锁A,就同样出现死锁了!!

一旦出现死锁,线程就“卡住了”无法继续工作,死锁,属于程序中最严重的一类bug!!

java">import static java.lang.Thread.sleep;public class ThreadDemo17 {public static void main(String[] args) {Object A = new Object();Object B = new Object();Thread t1 = new Thread(()->{synchronized (A){//sleep 一下,是给 t2 时间,让 t2也能拿到 Btry {sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}   synchronized (B){System.out.println("t1 拿到了两把锁");}}});Thread t2 = new Thread(()->{synchronized (B){try {sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (A){System.out.println("t2 拿到了两把锁");}}});t1.start();t2.start();}
}

他们彼此在等待对方解锁,但结果一直等不到。(钥匙锁车里,车钥匙锁屋里)

3.N个线程M把锁

哲学家就餐问题(学校的操作系统课,也有这个东西)

如果某个哲学家,在吃面条的过程中,旁边有两个老哥,就需要阻塞等待~~

(五双筷子,相当于五把锁)

五个老哥,就是应该线程。

当前线程拿到锁的时候,就会一直持有,除非他吃完,主动放下筷子,其他伙计不能硬抢!!

虽然筷子数量不多不充裕,但也还好~~

每个哲学家,除了吃面之外,还要做一件事,“思考人生”,是会放下筷子的~~

由于每个哲学家,啥时候吃面条,啥时候思考人生,这个事情不确定~~(随机调度)

绝大部分下,上述模型都是可以正常工作的~~

但是有一些极端的特殊情况,是不行的!!

解决死锁问题,方法有很多。

先解释一下,产生死锁的四个必要条件

2.产生死锁的四个必要条件

1.互斥使用。获取锁的过程是互斥的, 一个线程拿到了这把锁,另一个线程也想获取就需要阻塞等待。锁基本的特性,不太好破坏~~

2.不可抢占。一个线程拿到了锁之后,只能主动解锁,不能让别的线程强行把锁带走~~锁基本的特性,不太好破坏~~

3.请求保持。一个线程拿到了锁A之后,在持有锁A前提下,尝试获取锁B。代码结构不一定,看实际需求~~~

4.循环等待/环路等待 代码结构的,最容易破坏

只要指定一定的规则,就可以有效避免循环等待

指定加锁顺序

针对五把锁,都进行编号,约定每个线程获取锁的时候,一定都先获取编号小的锁,后就获取编号大的锁!!!

上述这样调一下就好了,大前提,是“随机调度”想办法,让某个线程先加锁,未被“随机调度”根本原则可行性是不高的~~~

而约定加锁顺序,在写代码的层面上是非常容易做到的~~

3.解决死锁方案

1.引入额外的筷子(虽然方案复杂性不高,但普适性不强)

2.去掉一个线程(虽然方案复杂性不高,但普适性不强)

3.引入计数器,限制最多同时多少人吃面(虽然方案复杂性不高,但普适性不强)

4.引入加锁顺序的规则  (普适性高,方案容易落地)

5.学校操作系统课“银行家算法”(理论可行,实践不推荐)


http://www.ppmy.cn/news/1543388.html

相关文章

Django-中间件

定义: 编写中间件: 注册中间件: 添加中间件: 1.在项目目录下添加一个文件夹(名字随意),然后文件夹下创建.py文件 2.将中间件添加到setting文件中 MIDDLEWARE [django.middleware.security.Se…

2024年【北京市安全员-A证】考试题及北京市安全员-A证复审考试

题库来源:安全生产模拟考试一点通公众号小程序 北京市安全员-A证考试题是安全生产模拟考试一点通总题库中生成的一套北京市安全员-A证复审考试,安全生产模拟考试一点通上北京市安全员-A证作业手机同步练习。2024年【北京市安全员-A证】考试题及北京市安…

Word试题快速转换制作excel题库

一、问题描述 一些考证培训类的APP经常需要excel表格类型的题库,其结构如下图所示。从左往右分别是“题干”,“选项A”,“选项B”,“选项C”,“选项D”,“正确答案”,“解析”。 往往&#xff…

C语言:水仙花树,要求三位以上的N位整数每位的N次方等于数本身,全部输出出来

#include <stdio.h> int main() { int n; scanf("%d",&n);//这里是说明多少n位整数 int first1; int i1; while(i<n){//此while循环可以得到n位数的最小位,例如3位的100. first*10; i; } ifirst; whil…

禾川SV-X2E A伺服驱动器参数设置——脉冲型

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff01;人工智能学习网站 前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任…

python之爬虫遇到返回内容乱码

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言解决思路 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 问题复现&#xff1a; import requestsheaders {"Accept": &quo…

坏块处理 ORA-01578: ORACLE data block corrupted (file # 3, block # 152588)

帮客户检查环境时&#xff0c;发现sysaux表空间的数据文件有坏块&#xff0c;8月25日发生的&#xff0c;备份保留3个月&#xff0c;直接恢复处理。 rman备份log报错如下 RMAN-00571: RMAN-00569: ERROR MESSAGE STACK FOLLOWS RMAN-00571: RMAN-03009: failure of backu…

Spring Boot助力学生宿舍信息管理自动化

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…