当前位置: 首页 > news >正文

线程安全(二)--死锁

@TOC

一:什么是死锁???

public class Demo1 {public static void main(String[] args) {Object locker=new Object();Thread thread=new Thread(()->{synchronized(locker){synchronized (locker){System.out.println("hello thread");}}});thread.start();}
}

上述代码:thread一共加了两次锁,第一次加锁,肯定是能够成功的,但当第二次加锁的时候,此时,第一次加锁后,还未进行解锁操作,那么第二次加锁操作就不能获得锁对象,就会加锁失败,那么该线程就会进入阻塞等待,等待到第一次加锁 操作完了,释放锁操作,但第一次的操作要释放锁,那么必须执行完第二次加锁,解锁操作(代码顺序执行).
这样就会造成第二次加锁操作阻塞等待,等第一次操作释放锁,由于代码顺序执行,第一次要想释放锁,那必须执行完第二次的synchronized操作,那么就非常矛盾,这种情况,就叫做死锁.

二:可重入锁

当我们运行程序的时候,发现代码正常执行了,并没有进入死锁状态.
这是为什么???
是因为synchronized,对上述情况做出处理(JVM).
每个锁对象里,会记录当前是哪个线程持有了这个锁,当针对这个锁对象进行加锁操作的时候,就会先判定一下,当前尝试加锁的线程,是否是持有锁的线程,如果是,直接进行加锁操作,如果不是,那就阻塞等待.这种锁也就作可重入锁

synchronized(locker){synchronized (locker){System.out.println("hello thread");}//1}//2

上面代码,当加了2层锁的时候,代码执行到哪里是要真正的进行解锁操作呢?
肯定是在2这里解锁(最外层的}),否则,如果在1解锁,那么1和2中间有代码的话,是没有被保护起来的.
同理:如果加了N层锁,是如何判定遇到的**}是最外层的呢?
其实,JVM会给锁对象维护一个计数器(int n),每次遇到
{** 就加1,(只有第一次才是真正的加锁),每次遇到**}**,n就-1,当n=0的时候,就是真正的解锁.

三:死锁的典型场景:

1:场景一:不可重入锁

锁是不可重入锁,并且一下线程针对一个锁对象,连续加锁多次,
引入可重入锁,问题就解决了.

2:场景二:两个线程,两把锁

有线程1和线程2,锁A和锁B;现在,线程1拿到了锁A,线程2拿到了锁B,然后线程1想要获取锁B,就需要阻塞等待,等待线程2 释放锁B,同理线程2想要获取到锁A,就需要等待线程1释放锁A,两个线程都进入阻塞等待,那么就会引起死锁问题.

public class Demo2 {public static void main(String[] args) throws InterruptedException {Object locker1=new Object();Object locker2=new Object();Thread t1=new Thread(()->{synchronized(locker1){try {Thread.sleep(1000);//sleep()是为了让t2线程拿到另一把锁} catch (InterruptedException e) {e.printStackTrace();}synchronized (locker2){System.out.println("hello t1");}}});Thread t2=new Thread(()->{synchronized(locker2){synchronized (locker1){System.out.println("hello t2");}}});t1.start();t2.start();}
}

此时:t1 尝试对locker2加锁,就会阻塞等待,等待t2释放locker2;t2尝试对locker1加锁,也会阻塞等待,等待t1释放locker1.
在这里插入图片描述
在这里插入图片描述
代码执行结果:进程没有退出,也没有打印任何的内容,死锁了.
通过jconslole可以看到这里线程的状态:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3:场景三:N个线程,M把锁

哲学家就餐问题:
每个滑稽都坐在两个筷子之间.每个滑稽要做两件事:
1:思考人生(放下筷子),
2:吃面条(拿起左右两根筷子)
每个哲学家什么时候吃面条,什么时候思考人生,都是不确定的(线程抢占式执行)
那么就会出现极端情况,同一时刻,所以的滑稽都拿起左边的筷子,此时,所以的滑稽都无法拿起右边的筷子,并且每个滑稽都是固执的人(每个哲学家只有吃不到面条,绝不会放下手中的筷子),那么就会引起死锁问题

四:如何解决死锁问题???

死锁,是非常严重的问题,就会使线程阻塞等待,使线程卡住了,没法正常工作了,更可怕的是,死锁这种bug,往往都是概率性出现.
那么就必须解决死锁问题,同时线程安全问题也必须解决.

4.1:死锁的4个必要条件

1:锁具有互斥特性(锁的基本特点,一个线程拿到锁之后,其他线程要想拿到这个锁,就必须阻塞等待).
2:锁不可抢占(不可被剥夺):一个线程拿到锁之后,除非它自己主动释放锁,否则其他线程抢不走(锁的基本特点).
3:请求和保持(嵌套锁):一个线程拿到一把锁之后,不释放这个锁的前提下,再尝试获取别的锁.
4:循环等待:多个线程获取多个锁的过程中,出现了循环等待.A线程等待B,B线程等待A.
若要构成死锁,这4个条件缺一不可,那么如果要解决死锁问题,就要从这四个条件下手.而条件1和条件2,是锁的基本特性,无法修改.

4.2:从条件3解决死锁问题:

public class Demo2 {public static void main(String[] args) throws InterruptedException {Object locker1=new Object();Object locker2=new Object();Thread t1=new Thread(()->{synchronized(locker1){try {Thread.sleep(1000);//sleep()是为了让t2线程拿到另一把锁} catch (InterruptedException e) {e.printStackTrace();}}synchronized (locker2){System.out.println("hello t1");}});Thread t2=new Thread(()->{synchronized(locker2){}synchronized (locker1){System.out.println("hello t2");}});t1.start();t2.start();}
}

在这里插入图片描述
但有的情况下,嵌套锁的情况必须存在,那么只好用另一种方法了.

4.3:从条件4解决死锁问题:

public class Demo3 {public static void main(String[] args) {Object locker1=new Object();Object locker2=new Object();Thread t1=new Thread(()->{synchronized (locker1){synchronized (locker2){System.out.println(" t1获得了两把锁");}}});Thread t2=new Thread(()->{synchronized (locker1){synchronized (locker2){System.out.println(" t2获得了两把锁");}}});t1.start();t2.start();}
}

在这里插入图片描述

相关文章:

  • C#_事件_多线程(基础)
  • CCF考级 1-8级考纲知识点
  • 面试吹牛宝典
  • Linux内核err.h文件分析
  • springboot基本使用八(mbatisplus+filter实现登录功能)
  • ADC重要的信噪比公式是怎么来的?
  • Python自动连接SSH
  • Redis入门三(主从复制、Redis哨兵、Redis集群、缓存更新策略、缓存穿透、缓存击穿、缓存雪崩)
  • 算法学习——LeetCode力扣动态规划篇8
  • MATLAB 自定义生成直线点云(详细介绍) (47)
  • JSQLParserException异常
  • OpenHarmony无人机MAVSDK开源库适配方案分享
  • Mac 装 虚拟机 vmware、centos7等
  • phpspreadsheet导出Excel报错问题汇总
  • 运筹学基础(二):求解整数规划的分支定界法(branch and bound)
  • 【108天】Java——《Head First Java》笔记(第1-4章)
  • 【知识碎片】第三方登录弹窗效果
  • iOS高仿微信项目、阴影圆角渐变色效果、卡片动画、波浪动画、路由框架等源码...
  • IP路由与转发
  • LeetCode29.两数相除 JavaScript
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • 前端技术周刊 2019-02-11 Serverless
  • 小李飞刀:SQL题目刷起来!
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​ 轻量应用服务器:亚马逊云科技打造全球领先的云计算解决方案
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • #100天计划# 2013年9月29日
  • #周末课堂# 【Linux + JVM + Mysql高级性能优化班】(火热报名中~~~)
  • (10)STL算法之搜索(二) 二分查找
  • (C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切
  • (LeetCode) T14. Longest Common Prefix
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (二)换源+apt-get基础配置+搜狗拼音
  • (二)丶RabbitMQ的六大核心
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (附源码)ssm旅游企业财务管理系统 毕业设计 102100
  • (十二)python网络爬虫(理论+实战)——实战:使用BeautfulSoup解析baidu热搜新闻数据
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • ./mysql.server: 没有那个文件或目录_Linux下安装MySQL出现“ls: /var/lib/mysql/*.pid: 没有那个文件或目录”...
  • .net framwork4.6操作MySQL报错Character set ‘utf8mb3‘ is not supported 解决方法
  • .net 中viewstate的原理和使用
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题
  • // an array of int
  • /boot 内存空间不够
  • /etc/sudoer文件配置简析
  • ::before和::after 常见的用法
  • @在php中起什么作用?
  • [android] 天气app布局练习
  • [CakePHP] 在Controller中使用Helper
  • [Contest20180313]灵大会议
  • [CTO札记]如何测试用户接受度?
  • [CVPR2021]Birds of a Feather: Capturing Avian Shape Models from Images
  • [Excel]如何找到非固定空白格數列的條件數據? 以月份報價表單為例
  • [hdu 2826] The troubles of lmy [简单计算几何 - 相似]
  • [iOS]把16进制(#871f78)颜色转换UIColor