死锁产生条件

死锁的产生必须具备以下四个条件:

条件
互斥 指线程对已经获取到的资源进行排它性使用,即该资源同时只由一个线程占用。如果此时还有其他线程请求获取该资源,则请求者只能等待,直至占有资源的线程释放该资源。
占有且等待 指一个线程已经持有了至少一个资源,但又提出了新的资源请求,而新资源已被其他线程占有,所以当前线程会被阻塞,但阻塞的同时并不释放自己已经获取的资源。
不可抢占 指线程获取到的资源在自己使用完之前不能被其他线程抢占,只有在自己使用完毕后才由自己释放该资源。
循环等待 指在发生死锁时,必然存在一个线程—资源的环形链,即线程集合{T0, T1, T2, …, Tn}中的T0正在等待一个T1占用的资源,T1正在等待T2占用的资源,……Tn正在等待已被T0占用的资源
  1. package com.example.thread;
  2. /**
  3. * @Description:
  4. * @Author: baxiang
  5. * @Date: 2021/9/28.
  6. */
  7. public class DeadLockTest {
  8. //1. 创建共享资源
  9. private static Object resourceA = new Object();
  10. private static Object resourceB = new Object();
  11. public static void main(String[] args) {
  12. //2. 创建线程A
  13. Thread threadA = new Thread(new Runnable() {
  14. @Override
  15. public void run() {
  16. //2.1获取资源A的锁
  17. synchronized (resourceA) {
  18. System.out.println(Thread.currentThread() + " get ResourceA");
  19. try {
  20. Thread.sleep(1000);
  21. } catch (InterruptedException e) {
  22. e.printStackTrace();
  23. }
  24. //2.1获取资源B的锁
  25. System.out.println(Thread.currentThread() + "waiting get ResourceB");
  26. synchronized (resourceB) {
  27. System.out.println(Thread.currentThread() + "get ResourceB");
  28. }
  29. }
  30. }
  31. });
  32. //3. 创建线程B
  33. Thread threadB = new Thread(new Runnable() {
  34. @Override
  35. public void run() {
  36. //3.1获取资源B的锁
  37. synchronized (resourceB) {
  38. System.out.println(Thread.currentThread() + " get ResourceB");
  39. try {
  40. Thread.sleep(1000);
  41. } catch (InterruptedException e) {
  42. e.printStackTrace();
  43. }
  44. //3.2获取资源A的锁
  45. System.out.println(Thread.currentThread() + "waiting get ResourceA");
  46. synchronized (resourceA) {
  47. System.out.println(Thread.currentThread() + "get ResourceA");
  48. }
  49. }
  50. ;
  51. }
  52. });
  53. //3. 启动线程
  54. threadA.start();
  55. threadB.start();
  56. }
  57. }

避免线程死锁

互斥这个条件我们没有办法破坏,因为我们用锁为的就是互斥,其他三个条件都有办法可以破坏
对于“占用且等待”这个条件,我们可以一次性申请所有的资源,这样就不存在等待了。
对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动
释放它占有的资源,这样不可抢占这个条件就破坏掉了。
对于“循环等待”这个条件,可以靠按序申请资源来预防。所谓按序申请,是指资源是有线性顺序
的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环
了。

目前只有请求并持有和环路等待条件是可以被破坏的。

  1. public class DeadLockBrokenTest {
  2. //1. 创建共享资源
  3. private static Object resourceA = new Object();
  4. private static Object resourceB = new Object();
  5. public static void main(String[] args) {
  6. //2. 创建线程A
  7. Thread threadA = new Thread(new Runnable() {
  8. @Override
  9. public void run() {
  10. //2.1获取资源A的锁
  11. synchronized (resourceA) {
  12. System.out.println(Thread.currentThread() + " get ResourceA");
  13. try {
  14. Thread.sleep(1000);
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. //2.1获取资源B的锁
  19. System.out.println(Thread.currentThread() + "waiting get ResourceB");
  20. synchronized (resourceB) {
  21. System.out.println(Thread.currentThread() + "get ResourceB");
  22. }
  23. }
  24. }
  25. });
  26. //3. 创建线程B
  27. Thread threadB = new Thread(new Runnable() {
  28. @Override
  29. public void run() {
  30. //3.1获取资源A的锁
  31. synchronized (resourceA) {
  32. System.out.println(Thread.currentThread() + " get ResourceA");
  33. try {
  34. Thread.sleep(1000);
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. //3.2获取资源B的锁
  39. System.out.println(Thread.currentThread() + "waiting get ResourceB");
  40. synchronized (resourceB) {
  41. System.out.println(Thread.currentThread() + "get ResourceB");
  42. }
  43. }
  44. }
  45. });
  46. //3. 启动线程
  47. threadA.start();
  48. threadB.start();
  49. }
  50. }