1、死锁发生的条件

  • 互斥
  • 环路
  • 请求并保持
  • 不可剥夺

2、死锁的影响

死锁在不同系统中是不一样的,这取决于系统对死锁的处理能力

  • 数据库:检测并放弃事务

  • jvm中:无法自动处理

3、发生死锁的例子

两个线程相互持有并等待锁

  1. /**
  2. * @Author: zhangjx
  3. * @Date: 2020/9/20 19:27
  4. * @Description: 必定发生死锁的例子
  5. */
  6. public class MustDeadLock implements Runnable{
  7. int falg = 1;
  8. static Object o1 = new Object();
  9. static Object o2 = new Object();
  10. @Override
  11. public void run() {
  12. if (falg == 1) {
  13. synchronized (o1){
  14. System.out.println("获取o1的锁");
  15. try {
  16. Thread.sleep(1000);
  17. } catch (InterruptedException e) {
  18. e.printStackTrace();
  19. }
  20. synchronized (o2){
  21. System.out.println("获取o2的锁");
  22. }
  23. }
  24. }
  25. if (falg == 0) {
  26. synchronized (o2){
  27. System.out.println("获取o2的锁");
  28. try {
  29. Thread.sleep(1000);
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. synchronized (o1){
  34. System.out.println("获取o1的锁");
  35. }
  36. }
  37. }
  38. }
  39. public static void main(String[] args) {
  40. MustDeadLock runnable1 = new MustDeadLock();
  41. runnable1.falg = 0;
  42. MustDeadLock runnable2 = new MustDeadLock();
  43. runnable2.falg = 1;
  44. new Thread(runnable1).start();
  45. new Thread(runnable2).start();
  46. }
  47. }

4、如何定位死锁

4.1 jstack(不是一定能检测出来)

1、查看java的pid
2、执行${JAVA_HOME}/bin/jstack pid

  1. Found one Java-level deadlock:
  2. =============================
  3. "Thread-1":
  4. waiting to lock monitor 0x000000002881fa38 (object 0x0000000716cdd788, a java.lang.Object),
  5. which is held by "Thread-0"
  6. "Thread-0":
  7. waiting to lock monitor 0x00000000259f0c88 (object 0x0000000716cdd778, a java.lang.Object),
  8. which is held by "Thread-1"
  9. Java stack information for the threads listed above:
  10. ===================================================
  11. "Thread-1":
  12. at com.imooc.thread_demo.deadlock.MustDeadLock.run(MustDeadLock.java:25)
  13. - waiting to lock <0x0000000716cdd788> (a java.lang.Object)
  14. - locked <0x0000000716cdd778> (a java.lang.Object)
  15. at java.lang.Thread.run(Thread.java:748)
  16. "Thread-0":
  17. at com.imooc.thread_demo.deadlock.MustDeadLock.run(MustDeadLock.java:39)
  18. - waiting to lock <0x0000000716cdd778> (a java.lang.Object)
  19. - locked <0x0000000716cdd788> (a java.lang.Object)
  20. at java.lang.Thread.run(Thread.java:748)
  21. Found 1 deadlock.

2、ThreadMXBean 代码

  1. package com.imooc.thread_demo.deadlock;
  2. import java.lang.management.ManagementFactory;
  3. import java.lang.management.ThreadInfo;
  4. import java.lang.management.ThreadMXBean;
  5. import java.util.Arrays;
  6. /**
  7. * @Author: zhangjx
  8. * @Date: 2020/9/20 21:14
  9. * @Description:
  10. */
  11. public class QryDeadLock implements Runnable{
  12. int falg = 1;
  13. static Object o1 = new Object();
  14. static Object o2 = new Object();
  15. @Override
  16. public void run() {
  17. if (falg == 1) {
  18. synchronized (o1) {
  19. System.out.println("获取o1的锁");
  20. try {
  21. Thread.sleep(1000);
  22. } catch (InterruptedException e) {
  23. e.printStackTrace();
  24. }
  25. synchronized (o2) {
  26. System.out.println("获取o2的锁");
  27. }
  28. }
  29. }
  30. if (falg == 0) {
  31. synchronized (o2) {
  32. System.out.println("获取o2的锁");
  33. try {
  34. Thread.sleep(1000);
  35. } catch (InterruptedException e) {
  36. e.printStackTrace();
  37. }
  38. synchronized (o1) {
  39. System.out.println("获取o1的锁");
  40. }
  41. }
  42. }
  43. }
  44. public static void main(String[] args) throws InterruptedException {
  45. QryDeadLock runnable1 = new QryDeadLock();
  46. runnable1.falg = 0;
  47. QryDeadLock runnable2 = new QryDeadLock();
  48. runnable2.falg = 1;
  49. new Thread(runnable1).start();
  50. new Thread(runnable2).start();
  51. Thread.sleep(1000);
  52. ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
  53. long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
  54. if(deadlockedThreads != null && deadlockedThreads.length > 0){
  55. Arrays.stream(deadlockedThreads).forEach(e -> {
  56. ThreadInfo threadInfo = threadMXBean.getThreadInfo(e);
  57. System.out.println("发现死锁 :" + threadInfo.getThreadName() + " " + threadInfo.getLockName());
  58. });
  59. }
  60. }
  61. }

5、如何修复死锁

1、避免策略
避免相反获取锁顺序的发生,用hash值决定两把锁获取的顺序
2、检测与恢复策略
一段时间检测是否有死锁,如果有就剥夺某个资源,打开死锁
3、鸵鸟策略(不处理)

6、实际工程中如何避免死锁

1、设置超时时间

Lock的tryLock(Long timeOut,TimeUnit unit)
syncronized不具备尝试锁的能力

获取锁失败:打日志,发邮件报警,重启等

  1. package com.imooc.thread_demo.deadlock;
  2. import java.util.concurrent.TimeUnit;
  3. import java.util.concurrent.locks.Lock;
  4. import java.util.concurrent.locks.ReentrantLock;
  5. /**
  6. * @Author: zhangjx
  7. * @Date: 2020/9/20 22:14
  8. * @Description: 设置超时时间解决死锁
  9. */
  10. public class TryLockDeadLock implements Runnable{
  11. static Lock lock1 = new ReentrantLock();
  12. static Lock lock2 = new ReentrantLock();
  13. int flag = 0;
  14. public static void main(String[] args) {
  15. TryLockDeadLock runnable1 = new TryLockDeadLock();
  16. runnable1.flag = 0;
  17. TryLockDeadLock runnable2 = new TryLockDeadLock();
  18. runnable2.flag = 1;
  19. new Thread(runnable1).start();
  20. new Thread(runnable2).start();
  21. }
  22. @Override
  23. public void run() {
  24. for (int i = 0; i < 10; i++) {
  25. if (flag == 1) {
  26. try {
  27. if(lock1.tryLock(1000, TimeUnit.MILLISECONDS)){
  28. System.out.println("线程1获取到第一把锁lock1");
  29. if(lock2.tryLock(1100, TimeUnit.MILLISECONDS)){
  30. System.out.println("线程1获取到两把锁,lock1,lock2");
  31. Thread.sleep(2000);
  32. System.out.println("线程1执行完毕,退出");
  33. lock2.unlock();
  34. lock1.unlock();
  35. break;
  36. }else {
  37. System.out.println("线程1获取第二把锁lock2失败,释放第一把锁lock1,重试");
  38. lock1.unlock();
  39. }
  40. }else {
  41. System.out.println("线程1获取第一把锁lock1失败,重试");
  42. }
  43. } catch (InterruptedException ex) {
  44. ex.printStackTrace();
  45. }
  46. }
  47. if (flag == 0) {
  48. try {
  49. if(lock2.tryLock(800, TimeUnit.MILLISECONDS)){
  50. System.out.println("线程2获取到第一把锁lock2");
  51. if(lock1.tryLock(900, TimeUnit.MILLISECONDS)){
  52. System.out.println("线程2获取到两把锁 lock2,lock1");
  53. Thread.sleep(1000);
  54. System.out.println("线程2执行完毕,退出");
  55. lock1.unlock();
  56. lock2.unlock();
  57. break;
  58. }else {
  59. System.out.println("线程2获取第二把锁lock1失败,释放第一把锁lock2,重试");
  60. lock2.unlock();
  61. }
  62. }else {
  63. System.out.println("线程2获取第一把锁释放第一把锁lock2失败,已重试");
  64. }
  65. } catch (InterruptedException ex) {
  66. ex.printStackTrace();
  67. }
  68. }
  69. }
  70. }
  71. }

2、多使用并发类,而不是自己去设计锁

  • ConcurrentLinkedDeque ,ConcurrentHashMap等
  • 实际应用中java.util.concurrent.atomic简单方便且效率比使用lock更高
  • 多使用并发集合少使用同步集合

3、尽量降低锁的使用粒度,用不同的锁而不是一个锁

4、尽量使用同步代码块代替同步方法,自己指定锁对象

5、给线程取有意义的名字

6、避免锁的嵌套

7、分配资源前看是否能够收回来,银行家算法

8、尽量不要不同功能使用同一个锁