线程安全(二)--死锁

@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();}
}

在这里插入图片描述

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

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

相关文章

深度学习入门简单实现一个神经网络

实现一个三层神经网络 引言测试数据 代码 引言 今天我们实现一个简单的神经网络 俩个输入神经元 隐藏层两个神经元 一个输出神经元 激活函数我们使用sigmoid 优化方法使用梯度下降 我们前期准备是需要把这些神经元的关系理清楚 x1:第一个输入 x2:第二个…

python中pow()函数的使用

在Python中,pow() 函数用于计算指定数字的幂。它的语法如下: pow(x, y) 这个函数返回 x 的 y 次方。相当于 x**y。 pow() 函数也可以接受一个可选的第三个参数,用于指定一个取模值,即计算结果与该模值的余数。其语法如下&#…

JSQLParserException异常

前言 SQL中加入了租户字段&#xff0c;报这个错&#xff0c;可以查出数据&#xff0c;但是不多&#xff1b;SQL检查无问题 解决 原因一 引入新的SQL解析器检查解析SQL&#xff0c;与mybatis多租户无关 参考 <!--jsqlparser版本太低也无法解析&#xff0c;如2.0--> &…

java Web洗衣店管理系统用eclipse定制开发mysql数据库BS模式java编程jdbc

一、源码特点 JSP 洗衣店管理系统是一套完善的web设计系统&#xff0c;对理解JSP java 编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,eclipse开发&#xff0c;数据库为Mysql5.0&#xff0c;使用…

Pillow教程05:NumPy数组和PIL图像的相互转化

---------------Pillow教程集合--------------- Python项目18&#xff1a;使用Pillow模块&#xff0c;随机生成4位数的图片验证码 Python教程93&#xff1a;初识Pillow模块&#xff08;创建Image对象查看属性图片的保存与缩放&#xff09; Pillow教程02&#xff1a;图片的裁…

集合(ArrayList,HashMap,HashSet)详解+ entrySet的应用

集合 例题引入——直线题意分析根据下面的参考代码&#xff0c;自己模仿的参考代码&#xff08;加一点点我的小tips&#xff09; 1.java集合引入2.为什么要使用集合&#xff1f;3.List、Set、Queue和Map的区别4.ListList——ArrayList&#xff08;&#xff01;&#xff01;实用…

24Compact模式启动

Compact模式启动 compact,是压缩的意思.顾名思义,这是一种压缩启动技术. Compact启动模式比wimboot出现得还要晚,是微软在Windows10中才引入的一种启动模式.相比于普通启动和wimboot,compact启动有以下几个优点: 1.减少占用C盘空间:compact把系统文件在C盘内直接进行压缩,从而达…

基于springboot的船舶维保管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

Java反序列化JDK动态代理的关系

Java代理模式 为什么要学习代理模式&#xff1f;了解开发原理&#xff0c;才能明白漏洞的产生。这不仅仅是SpringAOP的底层&#xff01; [SpringAOP 和 SpringMVC] 代理模式的分类&#xff1a; 静态代理动态代理 静态代理 角色分析&#xff1a; 抽象角色&#xff1a;一般会…

ElasticSearch、java的四大内置函数式接口、Stream流、parallelStream背后的技术、Optional类

第四周笔记 一、ElasticSearch 1.安装 apt-get install lrzsz adduser -m es 创建用户组&#xff1a; useradd *-m* xiaoming(用户名) *PS&#xff1a;追加参数-m* passwd xiaoming(用户名) passwd xiaoming 输入新的 UNIX 密码&#xff1a; 重新输入新的 UNIX 密码&…

帆软报表踩坑日记

最近公司项目要是使用报表&#xff0c;公司使用的是帆软这个国产软件&#xff0c;自己也是学习使用&#xff0c;在使用的过程中记一下问题以及解决方式 公司使用的是帆软8这个版本&#xff0c;比较老了。 首先是表格中的扩展&#xff0c;就是当我们根据数据库查询数据然后放到表…

Redis入门三(主从复制、Redis哨兵、Redis集群、缓存更新策略、缓存穿透、缓存击穿、缓存雪崩)

文章目录 一、主从复制1.单例redis存在的问题2.主从复制是什么&#xff1f;3.主从复制的原理4.主从搭建1&#xff09;准备工作2&#xff09;方式一3&#xff09;方式二 5.python中操作1&#xff09;原生操作2&#xff09;Django的缓存操作 二、Redis哨兵&#xff08;Redis-Sent…

Nginx负载均衡 ,6种常用方式。(新手必看)

nginx的负载均衡策略有六种&#xff1a; 1、轮询&#xff08;默认策略&#xff0c;nginx自带策略&#xff09;&#xff1a;我上面的例子就是轮询的方式&#xff0c;它是upstream模块默认的负载均衡默认策略。会将每个请求按时间顺序分配到不同的后端服务器。 http {upstream …

腾讯 tendis 替代 redis linux安装使用

下载地址 Tendis存储版 点击下载 linux 解压 tar -zxvf 安装包.tgz cd 解压安装包/scripts 启动 ./start.sh 停止 ./stop.sh 详细配置 修改 /scripts tendisplus.conf # tendisplus configuration for testing # 绑定本机IIP bind 192.168.31.112 port 51002 #设…

【CANN训练营笔记】Atlas 200I DK A2体验手写数字识别模型训练推理

环境介绍 开发板&#xff1a;Huawei Atals 200I DK A2 内存&#xff1a;4G NPU&#xff1a;Ascend 310B4 准备环境 下载编译好的torch_npu wget https://obs-9be7.obs.cn-east-2.myhuaweicloud.com/wanzutao/torch_npu-2.1.0rc1-cp39-cp39-linux_aarch64.whl pip3 install …

简易TCP服务器通信、IO多路复用(select、poll、epoll)以及reactor模式。

网络编程学习 简单TCP服务器通信TCP三次握手和四次挥手三次握手&#xff08;如下图&#xff09;常见问题&#xff1f; 四次挥手 client和server通信写法server端client端 通信双方建立连接到断开连接的状态转换怎么应对多用户连接&#xff1f;缺点 IO多路复用select优缺点 poll…

游戏行业行业竞争越来越激烈,遇到DDoS攻击遭受严重损失该如何解决

近年来&#xff0c;我们见证了数字化的快速发展&#xff0c;随着这样的发展&#xff0c;网络的威胁也逐渐增多&#xff0c;在网络攻击门槛不断降低&#xff0c;行业竞争越来越激烈&#xff0c;游戏行业的DDoS攻击如雨点般密集&#xff0c;在整个DDoS攻击的份额中&#xff0c;游…

前端日期组件layui使用,月模式

初学前端&#xff0c;实战总结 概要 有一个日期组件&#xff0c;我的谷歌浏览器选完日期后&#xff0c;偶尔获取不到最新数据&#xff0c;有一个客户&#xff0c;是经常出不来数据。 日期组件是Wdate&#xff1a;调用的方法是WdatePicker onpicking&#xff0c;代码片段如下…

ubuntu 安装 cloudcompare(两种方法)

方法一 &#xff1a;从 snap 安装 &#xff08;推荐&#xff09; 安装简单&#xff0c;基本上功能都有&#xff08;读写保存las&#xff0c;pcd&#xff0c;标注等&#xff09; 安装&#xff1a; sudo apt-get update sudo apt install snap sudo snap install cloudcompare…

c++使用类的一些注意事项

前言&#xff1a; 本篇内容为前面的补充&#xff0c;介绍了我们使用类时需要注意些什么以及一些编译器的优化&#xff0c;可能有些理解不到位或者错误&#xff0c;请斧正。 目录 前言&#xff1a; 1.再谈构造函数 2.&#xff08;c98&#xff09;隐式类型转换中的编译器的优…