1.什么是死锁?
先看看比较官方的解释:死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去;此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
通俗来说,就是多个进程之间互相持有对方想要的资源,但又都不放手,从而形成的一种僵持的状态。
跳出计算机概念,生活里其实不乏类似于这样死锁的概念。比如有两个互相竞争的死对头无意中得到了一个神人的馈赠,神人想要给这两人一个宝箱,但神人知道这两人是死对头,倘若把宝箱给出去,必定会抢得一死一伤的,因此神人是这样给的:给其中一个人一把开宝箱的钥匙,而另一个人怀揣着宝箱。这样,除非他们要么愿意让出自己占有的主动让给对方,要么抢夺对方,不然他们都不能得到宝箱里面的东西。如果他们都是倔牛,不让不抢,就是耗着(诶,就是玩儿)。这时,我们可以说,这种状态被僵持住了,即死锁。
2.死锁产生的条件?
上面的例子如果能理解,那么死锁条件也就能先懂一二了。
(1)互斥条件。
进程对所分配到的资源进行排他性使用,即在一段时间,某资源只能被一个进程占用。
就像一把钥匙只能被一个人拿着,一个宝箱也只能被一个人拿着。
(2)请求和保持条件
进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己获得的资源保持不变。”
都在求着对方给出钥匙或宝箱,但各自又想着“我想你给我,我反正不会先给”。
(3)不可抢占条件
进程已获得的资源在未使用完之前不能被抢占,只能在进程使用完时由自己释放。
就像这两人虽然是死对头,但又是武林中人,年轻人讲武德,都不主动抢对方的,但又坚守自己的东西。
(4)循环等待条件
简单来说,就是互相耗着呗。
3.用Java代码展现死锁过程
package com.example.zhan;
import java.io.ObjectInputStream;
public class Threadsafe02 {
public static void main(String[] args) {
Object o = new Object();
Object o1 = new Object();
MyThread3 myThread3 = new MyThread3(o, o1);
MyThread4 myThread4 = new MyThread4(o, o1);
myThread3.start();
myThread4.start();
}
}
class MyThread3 extends Thread{
Object o1;
Object o2;
public MyThread3(Object o1,Object o2){
this.o1=o1;
this.o2=o2;
}
@Override
public void run() {
synchronized (o1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){
}
}
}
}
class MyThread4 extends Thread{
Object o1;
Object o2;
public MyThread4(Object o1,Object o2){
this.o1=o1;
this.o2=o2;
}
@Override
public void run() {
synchronized (o2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){
}
}
}
}
上面,我用了synchronized关键字,之后也会详细介绍其用法,简单说这是一个锁,可以锁对象或方法或变量等。作用当进程访问对象时,可以保证对象的某些资源或对象本身不被其他进程访问。
我用了MyThread3和MyThread4两个线程去获取o1,o2两个资源。可以看到每个线程方法中我都用了两个synchronized嵌套对象o1,o2,表示只有某个进程要同时获取了o1,o2,synchronized才会被揭开。当然,为了防止运行那一刻,某一进程直接就把o1,o2拿走了,我就用了sleep去延缓。
结果:
可以看到,一直在运行,并且无结束状态。当然,也不可能结束了,这种僵持的时间,可真就堪称互相等到天荒地老的真爱了(doge)。