同步弊端:

  • 效率低
  • 如果出现了同步嵌套,就容易产生死锁问题

死锁问题及其代码重现

死锁:

  是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待的现象 

举例: 
中国人、美国人吃饭案例

正常情况: 
中国人:筷子两支 
美国人:刀和叉

现在: 
中国人:筷子一支,刀一把 
美国人:筷子一支,叉一把

产生死锁问题: 
中国人拿着刀的同时等着美国人把另一只筷子给他,美国人拿着一支筷子的同时等着中国人把刀给他

同步代码块的嵌套案例–重现死锁现象

//定义锁对象
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:生产者—先看看是否有数据,有就等待;没有就生产,然后通知消费者进行消费

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执行程序时的随机性 
解决方法: 
利用 等待 – 唤醒 机制,让两个线程交替执行

死锁问题的出现和解决相关推荐

  1. oracle中“ORA-00060: 等待资源时检测到死锁” 或存储过程编译卡死 解决方法

    oracle中"ORA-00060: 等待资源时检测到死锁" 或存储过程编译卡死 解决方法 参考文章: (1)oracle中"ORA-00060: 等待资源时检测到死锁& ...

  2. 死锁产生的原因以及解决方法

    死锁产生的原因以及解决方法 参考文章: (1)死锁产生的原因以及解决方法 (2)https://www.cnblogs.com/JimmyFanHome/p/9914562.html 备忘一下.

  3. mysql数据库死锁的产生原因及解决办法

    该文章为转载,如有侵权请及时联系 这篇文章主要介绍了mysql数据库锁的产生原因及解决办法,需要的朋友可以参考下 数据库和操作系统一样,是一个多用户使用的共享资源.当多个用户并发地存取数据 时,在数据 ...

  4. 深入理解MySQL8中死锁及线上故障解决

    深入理解MySQL8中死锁及线上故障解决 一.什么是死锁 死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象. 若无外力作用,事务都将无法推进下去. 解决死锁问题最简单的 ...

  5. 递归锁,死锁,使用递归锁解决死锁,信号量

    递归锁 互斥锁 from threading import Lock lock_1 = Lock() lock_1.acquire() lock_1.acquire() print(123) # 打印 ...

  6. 记一次死锁问题的排查和解决

         说起来这个事情还是挺悲催的,记得上周忙的不亦乐乎,目标是改动之前另外一个团队留下来的一坨代码中的一些bug,这个项目是做OLAP分析的.分为两个模块,逻辑server主要负责一些元数据的操作 ...

  7. 什么叫死锁?死锁案例?死锁必须满足哪些条件?如何定位死锁问题?有哪些解决死锁策略?哲学家问题?

    1.死锁是什么? 死锁一定发生在并发环境中,死锁是一种状态,当两个(或者多个线程)相互持有对方所需要的资源,却又都不主动释放手中持有的资源,导致大家都获取不到自己想要的资源,所有相关的线程无法继续执行 ...

  8. mysql锁问题排查_Mysql死锁问题如何排查和解决?

    前言 发生死锁了,如何排查和解决呢?本文将跟你一起探讨这个问题 准备好数据环境 模拟死锁案发 分析死锁日志 分析死锁结果 环境准备 数据库隔离级别: mysql> select @@tx_iso ...

  9. MySQL死锁产生的原因和解决方法

    前言 最近老顾经常碰到同事说,mysql又死锁了导致业务报错.今天我们就来聊聊死锁以及怎么解决 锁类型 mysql锁级别:页级.表级.行级 表级锁:开销小,加锁快:不会出现死锁:锁定粒度大,发生锁冲突 ...

最新文章

  1. 绝密 | 机器学习老手不会轻易告诉你的12件事儿
  2. 2017.10.9 JVM入门学习
  3. 【数据结构与算法】二叉树遍历
  4. GitLab 分享项目到指定小组或者指定用户
  5. Cisco ASA站点间穿越nat互相访问的实验
  6. vue router 跳转php,vue路由:路由跳转后怎么知道切换到那个router-view中
  7. 一加8系列新机有望亮相CES 2020:全系支持5G网络
  8. 费诺编码的gui页面设计_GUI设计和UI设计有什么区别?
  9. 下十页分页php,织梦二次开发实现栏目分页前十页后十页功能
  10. HTML怎么消除链接下划线,HTML怎么去掉超链接的下划线
  11. 域名服务器有什么作用?怎么查询域名服务器地址?
  12. loading图片实现等待的动画
  13. 前端单元测试到底要怎么写?看这一篇就够了
  14. 机器人,给我来一瓶82年的农夫山泉
  15. MySQL数据查询之多表查询
  16. verilog时钟使能
  17. php spider 开发文档,开发PHPSpider爬虫的常用工具
  18. IOS开发教程--Xcode6,CLLocationManager无法定位解决方案
  19. 小米商城项目分析(上)
  20. Java位运算,负数的二进制表示形式,int类型最大值为什么是2的31次方-1

热门文章

  1. 浅谈 Java 并发下的乐观锁
  2. 我的兄弟姐妹 ----- 转发的项目组一鬼才的作品。语不惊人死不休,只有瞻仰的份了……...
  3. 树和二叉树的概念、性质、计算
  4. Python之服务巡检
  5. fast无线路由器设置服务器,Fast迅捷无线路由器端口映射设置方法 | 192路由网
  6. zzuoj 10400: B.海岛争霸
  7. Monthly Expense
  8. 【项目总结】医疗化验单的OCR识别
  9. 联想在钛媒体峰会上提前泄漏了MOTO新杀器
  10. 年度最火购车方式网上购车平台购车,强势来袭!