我网上查到的死锁
好,如果你能看懂理解这句话,就没必要看下去了,你已经知道啥是死锁了
好,如果你还能理解这句句话,那你真没必要看下去了,你理解能力很强。再结合这些文章下面的一些代码例子。你就完全懂了
但是我没懂,或者说当时懂了,过段时间就忘了。
我理解的死锁
是的,就像 2个人拿枪互相指着,想要对方手上的枪
四个条件
为什么说这就是我理解的死锁呢?毕竟产生死锁,需要符合4个条件,那我理解的死锁,是否满足呢。下面来一一对应一下。(下面的4个条件,与上文的4个大致相似,但有细微区别,我采用《Java编程之美》里的描述)
tip:男人女人各为2个不同线程,2把抢为2个资源
一把手枪只能被一个人拿着,另外的人想要用,只能等现在正在使用的人丢了,或者从他手上抢过来。(条件1成立)
男的已经拿了一把枪,但是又提出了让女的把枪给自己的要求,而这把枪已经被女的拿着了。所以男的就等着,等的时候肯定不会把自己的枪给女方。(条件2成立)
2个人拿枪互指着,还能让对方剥夺了自己的枪?也许电影里可能有主角光环,但是两个线程可没有大鱼和小鱼的区别。(条件3成立)
很明显男的想拿女的手上的枪,女的想要拿男的手上的枪。(条件4成立)
如果你觉得2个人好像觉得条件4好像有点不好理解,那么
(假装明楼手上拿着枪指着明诚。。)
那,明诚想要明台手上的枪,明台想要明楼手上的枪,明楼想要明诚手上枪。。你看,是不是,T1->T2->T3->T1 成了个环形链呢。看他们站位也是个环。
纸上学来终觉浅,绝知此事要躬行
上代码
class LockDemo {
//创建资源
private static Object gunA = new Object();
private static Object gunB = new Object();
public static void main(String[] agrs) {
//创建线程
Thread threadMan = new Thread(new Runnable() {
@Override
public void run() {
synchronized (gunA) {
System.out.println(Thread.currentThread() + "男人拿到了枪A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "男人想要枪B");
synchronized (gunB) {
System.out.println(Thread.currentThread() + "男人拿到枪B");
}
}
}
});
Thread threadWoMan = new Thread(new Runnable() {
@Override
public void run() {
synchronized (gunB) {
System.out.println(Thread.currentThread() + "女人拿到了枪B");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "女人想要枪A");
synchronized (gunA) {
System.out.println(Thread.currentThread() + "女人拿到了枪A");
}
}
}
});
//启动线程
threadMan.start();
threadWoMan.start();
}
}
运行结果:
Thread[Thread-0,5,main]男人拿到了枪A
Thread[Thread-1,5,main]女人拿到了枪B
Thread[Thread-1,5,main]女人想要枪A
Thread[Thread-0,5,main]男人想要枪B
男女各自有一把枪指着对方,然后都试图获取对方手里的枪,但是没有后文。这就是一个死锁。
代码分析:
代码先创建了2把抢,也就是2个资源,并创建了2个线程(看结果可以知Thread-0为男,Thread-1为女)。男的先获取到一把枪后,线程sleep了1s,为的是保证女的也可以拿的枪。这样才可以继续后面的条件,这个时候,男的获得了枪A,女的获得了枪B(线程0获取了资源gunA的锁,线程1获取了资源gunB的锁)。男在拿到枪A后,视图获取枪B,而枪B已经被女的占用了,女的也视图获取枪A。显然也获取不到。于是陷入等待。也就产生了死锁。
所以\color{red}{所以}所以
2个人拿枪互相指着,想要对方手上的枪,就他妈的叫他妈的“死锁”\color{red}{2个人拿枪互相指着,想要对方手上的枪,就他妈的叫他妈的“死锁”}2个人拿枪互相指着,想要对方手上的枪,就他妈的叫他妈的“死锁”
如何打破死锁
我们来完善一下上面的场景,2个人找到了一个通往没有bug的世界的大门,但是大门紧闭,门口放了两把枪,只有同时拿到两把枪,才可以进入大门(人进去后,两把枪又回到了原来的地方)。那按照电视剧里的剧情,是不是就会发生上面这种场面呢。一人枪一把后,互补相让。
那么如何才可以避免这种僵局,让2个人都通往有bug的世界呢,其实可以一个人先拿着2把枪进去后,另一个人再去拿不就好了。
所以,打破死锁只要打破4个条件之一就行,而4个条件能打破的只有条件2“请求并持有”和条件4“环路等待”可以打破。
造成死锁的原因其实和上门例子里的场景很类似,和申请资源的顺序有很大原因。2个人如果有序的去拿枪进门。就避免了死锁的产生。代码如何体现呢?修改一下线程WoMan
Thread threadWoMan = new Thread(new Runnable() {
@Override
public void run() {
synchronized (gunA) {
System.out.println(Thread.currentThread() + "女人拿到了抢A");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread() + "女人想要枪B");
synchronized (gunB) {
System.out.println(Thread.currentThread() + "女人拿到了枪B");
}
}
}
});
运行结果
Thread[Thread-0,5,main]男人拿到了枪A
Thread[Thread-0,5,main]男人想要枪B
Thread[Thread-0,5,main]男人拿到了枪B
Thread[Thread-1,5,main]女人拿到了抢A
Thread[Thread-1,5,main]女人想要枪B
Thread[Thread-1,5,main]女人拿到了枪B
代码分析:
男女都去获取枪A。假设男先获取到枪A,则女就会被阻塞,陷入等待,而不会去获取枪B,男的获取到枪A后,再去获取枪B,也顺利的获取到了,进入了没有bug的世界。这个时候2把抢又回到了原来的地方(两把枪资源的锁被释放了)。女就可以去获取枪A再获取枪B了。
这里几个小点:
- 1.男女两个线程start后。不一定是先start的就先获取到资源枪A
- 2.男线程获取到枪A后,女线程不会去获取枪B,因为代码里女线程需要先获取枪A,而此时枪A被男线程获取了,女线程就陷入阻塞
- 3.男线程获取资源枪A后,获取资源枪B的时候,不会立即释放枪A。这里有个容易误解的地方,一个线程只能同时获取一个资源么,也就是一个线程同时只能持有一把锁么,答案为:不是的。 因为锁是针对“对象”的,锁和对象是一一对应的,所以线程可以同时持有多个锁。