java多线程--死锁问题

  有这样一个场景:一个中国人和一个外国人在一起吃饭,美国人拿了中国人的筷子,中国人拿了美国人的刀叉,两个人开始争执不休:

中国人:“你先给我筷子,我再给你刀叉!”

美国人:“你先给我刀叉,我再给你筷子!”

...........

  结果可想而知,两个人都吃不到饭。这个例子中的中国人和美国人相当于不同的线程,筷子和刀叉就相当于锁。两个线程在运行时都在等待对方的锁,这样便造成了程序的停滞,这种现象称为死锁。

一、死锁的定义

     多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

     所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

二、死锁产生的原因

  1. 系统资源的竞争
  2. 进程推进顺序非法进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。
  3. 信号量使用不当也会造成死锁。

三、java 死锁产生的四个必要条件:

  • 1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
  • 2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
  • 3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
  • 4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
 1 @SpringBootTest
 2 //DeadLockThread,实现Runnable接口
 3 class DeadLockThread implements Runnable {
 4     //定义Object类型的chopsticks锁对象
 5     static Object chopsticks = new Object();
 6     //定义Object类型的knifeAndFork锁对象
 7     static Object knifeAndFork = new Object();
 8     //定义Object类型的变量flag
 9     private boolean flag;
10     //定义有参的构造方法
11     DeadLockThread(boolean flag) {
12         this.flag = flag;
13     }
14 
15     //实现接口中的run()方法
16     public void run() {
17         if (flag) {
18             while (true) {
19                 //chopsticks锁对象上的同步代码块
20                 synchronized (chopsticks) {
21                     System.out.println(Thread.currentThread().getName() + "--if--chopsticks");
22                     //knifeAndFork锁对象上的同步代码块
23                     synchronized (knifeAndFork) {
24                         System.out.println(Thread.currentThread().getName() + "--if--knifeAndFork");
25                     }
26                 }
27             }
28         } else {
29             while (true) {
30                 //knifeAndFork锁对象上的同步代码块
31                 synchronized (knifeAndFork) {
32                     System.out.println(Thread.currentThread().getName() + "--else--knifeAndFork");
33                     //chopsticks锁对象上的同步代码块
34                     synchronized (chopsticks) {
35                         System.out.println(Thread.currentThread().getName() + "--else--chopsticks");
36                     }
37                 }
38             }
39         }
40     }
41 }
42 public class Example {
43     public static void main(String[] args) {
44         //创建两个DeadLockThread对象
45         DeadLockThread d1 = new DeadLockThread(true);
46         DeadLockThread d2 = new DeadLockThread(false);
47         //创建并开启两个线程
48         new Thread(d1,"Chinese").start();
49         new Thread(d2,"American").start();
50     }
51  }

运行结果:

   创建了Chinese和American两个线程,分别执行了run()方法中if和else代码块中的同步代码块。Chinese线程拥有chopsticks锁,只有获得knifeAndFork锁才能执行完毕,而American拥有knifeAndFork锁,只有获得chopsticks锁才能执行完毕,两个线程都需要对方所占用的锁,但是都无法释放自己所拥有的锁,于是两个线程都处于了挂起状态,从而造成了死锁。

四、死锁的解决方法

  1. 加锁顺序(线程按顺序加锁)
  2. 加锁时限(线程尝试获取锁的时候加上时间,超过时间则放弃对该锁的请求,并释放自己占有的锁)
  3. 死锁检测

  说实话避免死锁还得再自己写代码的时候注意一下.这里引用别人的解决方法,不过我对于这些解决方法不是太懂,讲的太含糊没有具体的实例.

原文地址:https://www.cnblogs.com/wx60079/p/13361482.html