死锁问题的出现和解决
同步弊端:
- 效率低
- 如果出现了同步嵌套,就容易产生死锁问题
死锁问题及其代码重现
死锁:
是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待的现象
举例:
中国人、美国人吃饭案例
正常情况:
中国人:筷子两支
美国人:刀和叉
现在:
中国人:筷子一支,刀一把
美国人:筷子一支,叉一把
产生死锁问题:
中国人拿着刀的同时等着美国人把另一只筷子给他,美国人拿着一支筷子的同时等着中国人把刀给他
同步代码块的嵌套案例–重现死锁现象
//定义锁对象
public class MyLock {public static final Object objA = new Object();public static final Object objB = new Object();
}//定义Thread类的run()方法
public class DeadLock extends Thread {//定义一个flag变量,用来标记此时持有的是哪个锁private boolean flag;public DeadLock(boolean flag){this.flag = flag;}@Overridepublic void run() {if(flag){synchronized (MyLock.objA) {System.out.println("if objA");synchronized (MyLock.objB) {System.out.println("if objB");}}}else{synchronized (MyLock.objB) {System.out.println("if objB");synchronized (MyLock.objA) {System.out.println("if objA");}}}}
}//测试用例
public class DeadLockDemo {public static void main(String[] args) {DeadLock dl1 = new DeadLock(true);DeadLock dl2 = new DeadLock(false);dl1.start();dl2.start();}}
- 两个线程一旦启动,则将陷入互相等待的循环之中,就成为了死锁
死锁问题的解决
解决方式1:线程间通信–通过构造方法共享数据
有一个Student类作为资源(锁)
public class Student {/** 默认权限修饰符,是为了让同一个包下的其他类也能访问到其成员变量*/String name;int age;boolean flag; //默认情况下false:没有数据,如果是true:说明有数据
}
模拟生产者-消费者模型:
创建两个线程对象,一个是设置 Student 属性的线程 SetThread ,另一个是获取 Student 属性的线程 GetThread
public class SetThread implements Runnable {private Student st ;private int x=0;public SetThread(Student stu) {this.st = stu;}@Overridepublic void run() {while(true){synchronized (st) {//判断有没有数据if(st.flag){try {st.wait(); //如果有数据就等待消费者消费} catch (InterruptedException e) {e.printStackTrace();}}if(x%2 == 0){st.name = "紫霞仙子";st.age=27;}else{st.name = "刘意";st.age=30;}x++;//修改标记st.flag = true;//唤醒线程st.notify();}}}}public class GetThread implements Runnable {private Student st ;public GetThread(Student stu) {this.st = stu;}@Overridepublic void run() {while(true){synchronized (st) {//加锁,防止出现姓名--年龄不匹配的问题if(!st.flag){ //没有数据等待try {st.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(st.name+"--------"+st.age);//修改标记st.flag = true;//唤醒线程st.notify();}}}}
测试用例:
public class StudentDemo {public static void main(String[] args) {Student stu = new Student();SetThread st = new SetThread(stu);GetThread gt = new GetThread(stu);Thread t1 = new Thread(st);Thread t2 = new Thread(gt);t1.start();t2.start();}}
运行结果:
...
紫霞仙子--------27
紫霞仙子--------27
紫霞仙子--------27
紫霞仙子--------27
紫霞仙子--------27
刘意--------30
刘意--------30
刘意--------30
刘意--------30
刘意--------30
...
- 发现结果并不是那么理想,同一组数据出现多次
出现这种状况的原因:
CPU的一点点时间片执行时间,足以让一个线程中的代码执行多次
解决方式2:改进1:通过构造方法共享数据,并使用 (等待–唤醒) 机制实现线程间通信
存在问题:
A:如果消费者先抢到了CPU的执行权,就会去消费数据,但是现在的数据是默认值。
B:如果生产者先抢到CPU的执行权,就会去生产数据,但是生产完数据之后,还拥有执行权它会继续生产。
等待唤醒机制
A:生产者—先看看是否有数据,有就等待;没有就生产,然后通知消费者进行消费
B:消费者—先看看是否有数据,有就消费,消费完通知生产者继续生产;没有就等待
Object类中提供了3个方法:
wait():等待notify():唤醒单个线程notifyAll():唤醒所有线程
问:wait和notify方法为什么不定义在Thread类上而是Object类上呢?
这些方法的调用必须通过锁对象来调用,而我们刚才使用的锁对象是任意对象
所以,这些方法必须定义在Object类中。
解决方式3:改进2:私有化锁对象的成员变量,自己提供get和set方法供外界调用
方式2中,同样也存在不适用的情况:一旦Student类中的成员变量被private修饰符私有化,那么其他Thread类就无法直接访问到其成员变量了。
解决方法:
Student自己实现get和set方法,外部Thread类直接调用其方法进行数据交换即可。
public class Student {/** 一旦权限修饰符改为private,其他类就无法访问到* 改进:* 将set和get方法写在 Student类中*/String name;int age;boolean flag; //默认情况下没有数据,如果是true,说明有数据//student的set和get过程(包括同步)都是由自己来操作,外界调用就行public synchronized void set(String name, int age){//如果有数据就等待if(this.flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//设置数据this.name = name;this.age = age;//修改标记并唤醒线程this.flag = true;this.notify();}public synchronized void get(){//如果没有数据则等待if(!this.flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//获取数据System.out.println(this.name +"---"+this.age);//修改标记并唤醒线程this.flag = false;this.notify();}
}/*** 生产者类* @author llj**/public class SetThread implements Runnable {private Student st ;private int x=0;public SetThread(Student stu) {this.st = stu;}@Overridepublic void run() {while(true){if(x%2 == 0){st.set("紫霞仙子", 27) ;}else{st.set("刘意", 30) ;}x++;}}
}/*** 消费者类* @author llj**/
public class GetThread implements Runnable {private Student st ;public GetThread(Student stu) {this.st = stu;}@Overridepublic void run() {while(true){st.get();}}}测试用例:
public class StudentDemo {public static void main(String[] args) {Student stu = new Student();//将student引用作为参数传递实现线程间通信SetThread st = new SetThread(stu);GetThread gt = new GetThread(stu);Thread t1 = new Thread(st);Thread t2 = new Thread(gt);t1.start();t2.start();}
}
- 运行结果:
紫霞仙子---27
刘意---30
紫霞仙子---27
刘意---30
紫霞仙子---27
刘意---30
紫霞仙子---27
刘意---30
紫霞仙子---27
刘意---30
紫霞仙子---27
刘意---30
...
总结
A.姓名和年龄出现不匹配
原因:
线程运行的随机性
解决方法:
加锁,在一个线程对共享资源操作的时候,其他线程只能等待该锁释放
* 注意: A: 不同种类的线程都要加锁
B: 不同种类的线程加的锁都必须是同一把
B.执行结果重复出现
原因:
CPU的一点点时间片执行时间,足以让一个线程中的代码执行多次
CPU执行程序时的随机性
解决方法:
利用 等待 – 唤醒 机制,让两个线程交替执行
死锁问题的出现和解决相关推荐
- oracle中“ORA-00060: 等待资源时检测到死锁” 或存储过程编译卡死 解决方法
oracle中"ORA-00060: 等待资源时检测到死锁" 或存储过程编译卡死 解决方法 参考文章: (1)oracle中"ORA-00060: 等待资源时检测到死锁& ...
- 死锁产生的原因以及解决方法
死锁产生的原因以及解决方法 参考文章: (1)死锁产生的原因以及解决方法 (2)https://www.cnblogs.com/JimmyFanHome/p/9914562.html 备忘一下.
- mysql数据库死锁的产生原因及解决办法
该文章为转载,如有侵权请及时联系 这篇文章主要介绍了mysql数据库锁的产生原因及解决办法,需要的朋友可以参考下 数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据 ...
- 深入理解MySQL8中死锁及线上故障解决
深入理解MySQL8中死锁及线上故障解决 一.什么是死锁 死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象. 若无外力作用,事务都将无法推进下去. 解决死锁问题最简单的 ...
- 递归锁,死锁,使用递归锁解决死锁,信号量
递归锁 互斥锁 from threading import Lock lock_1 = Lock() lock_1.acquire() lock_1.acquire() print(123) # 打印 ...
- 记一次死锁问题的排查和解决
说起来这个事情还是挺悲催的,记得上周忙的不亦乐乎,目标是改动之前另外一个团队留下来的一坨代码中的一些bug,这个项目是做OLAP分析的.分为两个模块,逻辑server主要负责一些元数据的操作 ...
- 什么叫死锁?死锁案例?死锁必须满足哪些条件?如何定位死锁问题?有哪些解决死锁策略?哲学家问题?
1.死锁是什么? 死锁一定发生在并发环境中,死锁是一种状态,当两个(或者多个线程)相互持有对方所需要的资源,却又都不主动释放手中持有的资源,导致大家都获取不到自己想要的资源,所有相关的线程无法继续执行 ...
- mysql锁问题排查_Mysql死锁问题如何排查和解决?
前言 发生死锁了,如何排查和解决呢?本文将跟你一起探讨这个问题 准备好数据环境 模拟死锁案发 分析死锁日志 分析死锁结果 环境准备 数据库隔离级别: mysql> select @@tx_iso ...
- MySQL死锁产生的原因和解决方法
前言 最近老顾经常碰到同事说,mysql又死锁了导致业务报错.今天我们就来聊聊死锁以及怎么解决 锁类型 mysql锁级别:页级.表级.行级 表级锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突 ...
最新文章
- 绝密 | 机器学习老手不会轻易告诉你的12件事儿
- 2017.10.9 JVM入门学习
- 【数据结构与算法】二叉树遍历
- GitLab 分享项目到指定小组或者指定用户
- Cisco ASA站点间穿越nat互相访问的实验
- vue router 跳转php,vue路由:路由跳转后怎么知道切换到那个router-view中
- 一加8系列新机有望亮相CES 2020:全系支持5G网络
- 费诺编码的gui页面设计_GUI设计和UI设计有什么区别?
- 下十页分页php,织梦二次开发实现栏目分页前十页后十页功能
- HTML怎么消除链接下划线,HTML怎么去掉超链接的下划线
- 域名服务器有什么作用?怎么查询域名服务器地址?
- loading图片实现等待的动画
- 前端单元测试到底要怎么写?看这一篇就够了
- 机器人,给我来一瓶82年的农夫山泉
- MySQL数据查询之多表查询
- verilog时钟使能
- php spider 开发文档,开发PHPSpider爬虫的常用工具
- IOS开发教程--Xcode6,CLLocationManager无法定位解决方案
- 小米商城项目分析(上)
- Java位运算,负数的二进制表示形式,int类型最大值为什么是2的31次方-1