有这样一个场景:一个中国人和一个外国人在一起吃饭,美国人拿了中国人的筷子,中国人拿了美国人的刀叉,两个人开始争执不休:
中国人:“你先给我筷子,我再给你刀叉!”
美国人:“你先给我刀叉,我再给你筷子!”
...........
结果可想而知,两个人都吃不到饭。这个例子中的中国人和美国人相当于不同的线程,筷子和刀叉就相当于锁。两个线程在运行时都在等待对方的锁,这样便造成了程序的停滞,这种现象称为死锁。
一、死锁的定义
多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
二、死锁产生的原因
- 系统资源的竞争
- 进程推进顺序非法进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。
- 信号量使用不当也会造成死锁。
三、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锁才能执行完毕,两个线程都需要对方所占用的锁,但是都无法释放自己所拥有的锁,于是两个线程都处于了挂起状态,从而造成了死锁。
四、死锁的解决方法
- 加锁顺序(线程按顺序加锁)
- 加锁时限(线程尝试获取锁的时候加上时间,超过时间则放弃对该锁的请求,并释放自己占有的锁)
- 死锁检测
说实话避免死锁还得再自己写代码的时候注意一下.这里引用别人的解决方法,不过我对于这些解决方法不是太懂,讲的太含糊没有具体的实例.