1.  java中导致死锁的原因

  多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放,而该资源又被其他线程锁定,从而导致每一个线程都得等其它线程释放其锁定的资源,造成了所有线程都无法正常结束。这是从网上其他文档看到的死锁产生的四个必要条件:

  • 1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
  • 2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
  • 3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
  • 4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。

当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。下面用java代码来模拟一下死锁的产生。

模拟两个资源:

  1. public class ThreadResource
  2. {
  3. public static Object resource1 = new Object();
  4.  
  5. public static Object resource2 = new Object();
  6. }

模拟线程1占用资源1并申请获得资源2的锁:

  1. public class Thread1 implements Runnable
  2. {
  3.  
  4. @Override
  5. public void run()
  6. {
  7. try
  8. {
  9. System.out.println("Thread1 is running");
  10. synchronized (ThreadResource.resource1)
  11. {
  12. System.out.println("Thread1 lock resource1");
  13. Thread.sleep(2000);//休眠2s等待线程2锁定资源2
  14. synchronized (ThreadResource.resource2)
  15. {
  16. System.out.println("Thread1 lock resource2");
  17. }
  18. System.out.println("Thread1 release resource2");
  19. }
  20. System.out.println("Thread1 release resource1");
  21. }
  22. catch (Exception e)
  23. {
  24. System.out.println(e.getMessage());
  25. }
  26. System.out.println("Thread1 is stop");
  27. }
  28.  
  29. }

模拟线程2占用资源2并申请获得资源1的锁:

  1. public class Thread2 implements Runnable
  2. {
  3.  
  4. @Override
  5. public void run()
  6. {
  7. try
  8. {
  9. System.out.println("Thread2 is running");
  10. synchronized (ThreadResource.resource2)
  11. {
  12. System.out.println("Thread2 lock resource2");
  13. Thread.sleep(2000);//休眠2s等待线程1锁定资源1
  14. synchronized (ThreadResource.resource1)
  15. {
  16. System.out.println("Thread2 lock resource1");
  17. }
  18. System.out.println("Thread2 release resource1");
  19. }
  20. System.out.println("Thread2 release resource2");
  21. }
  22. catch (Exception e)
  23. {
  24. System.out.println(e.getMessage());
  25. }
  26. System.out.println("Thread2 is stop");
  27. }
  28.  
  29. }

同时运行俩个线程:

  1. public class ThreadTest
  2. {
  3. public static void main(String[] args)
  4. {
  5. new Thread(new Thread1()).start();
  6. new Thread(new Thread2()).start();
  7. }
  8. }

最后输出结果是:

Thread1 is running
Thread2 is running
Thread1 lock resource1
Thread2 lock resource2

并且程序一直无法结束。这就是由于线程1占用了资源1,此时线程2已经占用资源2,。这个时候线程1想要使用资源2,线程2想要使用资源1,。两个线程都无法让步,导致程序死锁。

2.  java避免死锁的解决意见

由上面的例子可以看出当线程在同步某个对象里,再去锁定另外一个对象的话,就和容易发生死锁的情况。最好是线程每次只锁定一个对象并且在锁定该对象的过程中不再去锁定其他的对象,这样就不会导致死锁了。比如将以上的线程改成下面这种写法就可以避免死锁:

  1. public void run()
  2. {
  3. try
  4. {
  5. System.out.println("Thread1 is running");
  6. synchronized (ThreadResource.resource1)
  7. {
  8. System.out.println("Thread1 lock resource1");
  9. Thread.sleep(2000);//休眠2s等待线程2锁定资源2
  10. }
  11. System.out.println("Thread1 release resource1");
  12. synchronized (ThreadResource.resource2)
  13. {
  14. System.out.println("Thread1 lock resource2");
  15. }
  16. System.out.println("Thread1 release resource2");
  17. }
  18. catch (Exception e)
  19. {
  20. System.out.println(e.getMessage());
  21. }
  22. System.out.println("Thread1 is stop");
  23. }

但是有的时候业务需要同时去锁定两个对象,比如转账业务:A给B转账,需要同时锁定A、B两个账户。如果A、B相互同时转账的话就会出现死锁的情况。这时可以定义一个规则:锁定账户先后的规则。根据账户的某一个属性(比如id或者hasCode),判断锁定的先后。即每一次转账业务都是先锁定A再锁定B(或者先锁定B在锁定A),这样也不会导致死锁的发生。比如按照上面的例子,需要同时锁定两个资源,可以根据资源的hashcode值大小来判断先后锁定顺序。可以这样改造线程:

  1. public class Thread3 implements Runnable
  2. {
  3.  
  4. @Override
  5. public void run()
  6. {
  7. try
  8. {
  9. System.out.println("Thread is running");
  10. if ( ThreadResource.resource1.hashCode() > ThreadResource.resource2.hashCode() )
  11. {
  12. //先锁定resource1
  13. synchronized (ThreadResource.resource1)
  14. {
  15. System.out.println("Thread lock resource1");
  16. Thread.sleep(2000);
  17. synchronized (ThreadResource.resource2)
  18. {
  19. System.out.println("Thread lock resource2");
  20. }
  21. System.out.println("Thread release resource2");
  22. }
  23. System.out.println("Thread release resource1");
  24. }
  25. else
  26. {
  27. //先锁定resource2
  28. synchronized (ThreadResource.resource2)
  29. {
  30. System.out.println("Thread lock resource2");
  31. Thread.sleep(2000);
  32. synchronized (ThreadResource.resource1)
  33. {
  34. System.out.println("Thread lock resource1");
  35. }
  36. System.out.println("Thread release resource1");
  37. }
  38. System.out.println("Thread release resource2");
  39. }
  40. }
  41. catch (Exception e)
  42. {
  43. System.out.println(e.getMessage());
  44. }
  45. System.out.println("Thread1 is stop");
  46. }
  47.  
  48. }

总结:死锁常见于,线程在锁定对象还没释放时,又需要锁定另一个对象,并且此时该对象可能被另一个线程锁定。这种时候很容易导致死锁。因此在开发时需要慎重使用锁,尤其是需要注意尽量不要在锁里又加锁。

注意:本文仅代表个人理解和看法哟!和本人所在公司和团体无任何关系!

java中多线程产生死锁的原因以及解决意见的更多相关文章

  1. Java进阶(四十二)Java中多线程使用匿名内部类的方式进行创建3种方式

    Java中多线程使用匿名内部类的方式进行创建3种方式 package cn.edu.ujn.demo; // 匿名内部类的格式: public class ThreadDemo { public st ...

  2. 2.1多线程(java学习笔记) java中多线程的实现(附静态代理模式)

    一.多线程 首先我们要清楚程序.进程.线程的关系. 首先进程从属于程序,线程从属于进程. 程序指计算机执行操作或任务的指令集合,是一个静态的概念. 但我们实际运行程序时,并发程序因为相互制约,具有“执 ...

  3. java中多线程执行时,为何调用的是start()方法而不是run()方法

    Thead类中start()方法和run()方法的区别 1,start()用来启动一个线程,当调用start()方法时,系统才会开启一个线程,通过Thead类中start()方法来启动的线程处于就绪状 ...

  4. Java中多线程的使用(超级超级详细)线程池 7

    Java中多线程的使用(超级超级详细)线程池 7 什么是线程池? 线程池是一个容纳多个线程的容器,线程池中的线程可以重复使用,无需反复创建线程而消耗过多的资源 *使用多线程的好处: 1.降低消耗,减少 ...

  5. Java中多线程的使用(超级超级详细)线程安全原理解析 4

    Java中多线程的使用(超级超级详细)线程安全 4 什么是线程安全? 有多个线程在同时运行,这些线程可能会运行相同的代码,程序运行的每次结果和单线程运行的结果是一样的,而且其他变量的值也和预期的值一样 ...

  6. com/opensymphony/xwork2/spring/SpringObjectFactory.java:220:-1问题出现的原因及解决办法

    转自:https://blog.csdn.net/shinchan_/article/details/37818927 com/opensymphony/xwork2/spring/SpringObj ...

  7. SpringBoot整合Swagger2案例,以及报错:java.lang.NumberFormatException: For input string: ""原因和解决办法

    原文链接:https://blog.csdn.net/weixin_43724369/article/details/89341949 SpringBoot整合Swagger2案例 先说SpringB ...

  8. Java中多线程原理详解

    Java是少数的集中支持多线程的语言之一,大多数的语言智能运行单独的一个程序块,无法同时运行不同的多个程序块,Java的多线程机制弥补了这个缺憾,它可以让不同的程序块一起运行,这样可以让程序运行更加顺 ...

  9. java中多线程详解-synchronized

    一.介绍 当多个线程涉及到共享数据的时候,就会设计到线程安全的问题.非线程安全其实会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”.发生脏读,就是取到的数据已经被其他的线 ...

随机推荐

  1. Draggable(拖动框)

    一.class加载方式 <div id="box" class="easyui-draggable" style="width:400px;he ...

  2. AD库转换为KiCAD库的方法

    AD库转换为KiCAD库的方法 参照博主另外一篇文档: AD转换为KiCAD的方法,点击此处

  3. mysql binlog相关

    1.清除的binlog   删除所有binlog日志,新日志编号从头开始 RESET MASTER;   删除mysql-bin.XXXX之前所有日志 PURGE MASTER LOGS TO 'my ...

  4. 【LeetCode】Heap

    [215] Kth Largest Element in an Array [Medium] 给个无序数组,返回第K大的数字. 方法1. 直接使用优先队列 priority_queue class S ...

  5. 【Luogu】【关卡2-1】简单的模拟(2017年10月)

    任务说明:开始普及组的训练!所谓模拟,就是直接根据题意编写,思维难度简单. 铺地毯 进制转换 多项式输出 机器翻译 排座椅 笨小猴 都是简单模拟题  

  6. 【记坑】Oracle数据库Date类型查询结果多出".0"的解决方法

    oracle设置数据库某张表的字段类型为date,数据库存值为 2019-11-25 18:51:47 格式,但是从数据库查询出来之后格式为 String stopTime = map.get(&qu ...

  7. 分考场(np完全问题,回溯法)

    问题描述 n个人参加某项特殊考试. 为了公平,要求任何两个认识的人不能分在同一个考场. 求是少需要分几个考场才能满足条件. 输入格式 第一行,一个整数n(1<n<100),表示参加考试的人 ...

  8. 深入理解Magento - 第六章 - 高级Magento模型

    我们讲过Magento有两种模型,简单模型和EAV(Entity Attribute Value)模型.上一章我们讲过所有的Magento模型都是继承自Mage_Core_Model_Abstract ...

  9. RMQ区间求最值

    RMQ用于区间快速查找最值,适用于期间数值无更改的情况.其预处理的复杂度为O(nlogn),查询的时间复杂度为O(1),对比于线段树的预处理O(nlogn),查询O(logn)来说,在某些情况下有着其 ...

  10. Linux命令篇-服务器查看日志(续)

    此文是继上文如何在服务器看日志的续集.之所以我觉得自己很菜,是因为我的周围都是大佬,他们都是值得我学习的对象. 通常大家看日志,无非两种,一是more命令,一是tail命令,其中的过程要么是翻页查看, ...