我们从一个比较形象的例子来了解什么是DeadLock:
狗剩和铁蛋两个小孩是好朋友,是烧一根玉米棒子都要从中间掰开,热乎乎的两个人对着啃那种。
不过好朋友也有闹矛盾的时候,这次在村头玩泥巴的时候就打架了,各自拉着对方的衣服不松手。
铁蛋还说谁先松手谁是狗,我们的狗剩哪能认怂,当即就答应了。
铁蛋的大爷在不远处乘凉,看见他俩打架就拄着拐棍挪了过来,
到跟前一看这形势就对自家大孙子说:“铁蛋,快把手松开”。
只见铁蛋喘着粗气,脸一迈,用后脑勺给他大爷回了一句:“他先松我再松”。
(这里我们注意铁蛋的想法:我正抓着狗剩的衣服,在狗剩松手之前,我是不会松开的。
—线程T1保持对资源R2的占用,同时请求资源R1,等待线程T2释放对资源R1的占用。)
铁蛋大爷又看向狗剩:“狗剩啊,你比他懂事,你先松开行不”。
狗剩吸溜了一下眼看流到嘴里的鼻涕:“他不松手我也不松”。
(狗剩的条件:只有铁蛋松手之后,我才会松开。—线程T2占用资源R1,等待线程T1释放资源R2。)
这样这俩就僵持在这儿了。
铁蛋大爷训到:“铁蛋,赶紧松开”。
铁蛋还气哼哼的:“凭啥我先,不松”,“松开”,“我就不”。
气的他大爷照他头上来了一拐棍,这下铁蛋立马哭着松手了,狗剩几乎也是同时松开,僵局被打破了。
我们把狗剩和铁蛋想象成两个线程:铁蛋T1、狗剩T2。资源:铁蛋衣服R1、狗剩衣服R2。
T1占用R2(铁蛋抓着狗剩衣服不放),请求资源R1(铁蛋想重新获取对自己衣服的掌控)。
狗剩当然也不甘示弱,僵持不下。
死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
这里的外力就是铁蛋大爷照铁蛋头上那一拐棍。
但是实际环境中的死锁往往要比这个复杂的多,有可能会是多个线程形成一个等待环路。
Java死锁产生的四个必要条件:
1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用。
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
如果不恰当地使用了锁,且要尝试锁住多个资源时,就可能会出现死锁情况。
package com.example.multithread;
/**
* @author yangwei
* @describition TODO
*
* @time 2020年8月
*/
public class DeadLock {
public static String R1 = "铁蛋的衣服";
public static String R2 = "狗剩的衣服";
public static void main(String[] args) throws Exception {
new Thread(new Thread1()).start();
Thread.sleep(1000);
new Thread(new Thread2()).start();
Thread.sleep(1000);
}
}
/**
* @author yangwei
* @describition 这是铁蛋
*
* @time 2020年8月
*/
class Thread1 implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
try {
System.out.println(System.currentTimeMillis() + "-T1开始运行!");
synchronized (DeadLock.R2) { // 抓住狗剩衣服
System.out.println(System.currentTimeMillis() + "-铁蛋抓住了" + DeadLock.R2 + "!");
Thread.sleep(3000); // 给狗剩时间让他抓住铁蛋衣服
synchronized (DeadLock.R1) { // 铁蛋尝试恢复对自己衣服的掌控
System.out.println(System.currentTimeMillis() + "-狗剩松开了" + DeadLock.R1 + "!");
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
/**
* @author yangwei
* @describition 这是狗剩
*
* @time 2020年8月
*/
class Thread2 implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
try {
System.out.println(System.currentTimeMillis() + "-T2开始运行!");
synchronized (DeadLock.R1) { // 抓住铁蛋衣服
System.out.println(System.currentTimeMillis() + "-狗剩抓住了" + DeadLock.R1 + "!");
Thread.sleep(3000); // 给铁蛋时间让他抓住狗剩衣服
synchronized (DeadLock.R2) { // 狗剩尝试恢复对自己衣服的掌控
System.out.println(System.currentTimeMillis() + "-铁蛋松开了" + DeadLock.R2 + "!");
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
我们可以看到从6:05开始到6:10,他们两个已经僵持5分钟了。
让铁蛋爷爷的拐棍再飞一会儿…
因为我得去挤公交了。。。
Semaphore是一种基于计数的信号量。
它可以设置线程竞争资源的超时时间,一旦超时,转而去做其它处理,而不是一直阻塞。