本章思维导图
死锁
死锁代表的是一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期阻塞,因此程序不可能正常终止(例如:客户一方占用资金等待货物资源,经销商一方占用货物资源等待资金,双方都占用一种资源,并且都在等待一种资源,这就导致交易流程无法正常进行,也就形成了所谓的“死锁”)
死锁产生的四个必要条件:
- 互斥使用:即当一个资源被一个一个线程使用(占有)时,别的线程不能使用
- 不可抢占:资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放
- 请求和保持:即当资源请求者在请求其他的资源的同时保持对原有资源的占有
- 循环等待:即存在一个等待循环队列,P1要P2的资源,P2要P1的资源,这样就形成了一个等待循环
当上述的四个条件都满足时,便形成死锁,当然,死锁的情况下如果打破上述任何一个条件,便可以让死锁消失。开发中应该尽量避免死锁
死锁在代码形式上通常需要锁的嵌套,也就是在锁的内部还有锁
代码实现:必然死锁的案例
package ThreadPool;
//必然死锁案例
public class ThreadDead {
// 定义两个资源对象
public static Object resource1=new Object();
public static Object resource2=new Object();
public static void main(String[] args) {
//定义两个线程
//线程1
new Thread(new Runnable() {
@Override
public void run() {
//上锁,第一次线程1锁住资源1
synchronized (resource1){
System.out.println("线程1占用资源1,请求资源2");
/*
休眠1s,这是为了保证死锁必然发生
以计算机的运行速度,保证了休眠1s的情况下,必然会执行线程2
此时资源1已经被上锁,待线程2开始运行,第一步就会讲资源2上锁
此时两个资源已经都被上锁
待两个线程休眠结束,开始运行
线程1进入锁阻塞状态,因为资源2已经被锁
线程2也进入锁阻塞状态,因为资源1已经被锁
两个线程都进入锁阻塞状态,无法正常结束,形成死锁
*/
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//上锁,第二次线程1锁住资源2
synchronized (resource2){
System.out.println("线程1成功占用资源2");
}
}
}
}).start();
//线程2
new Thread(new Runnable() {
@Override
public void run() {
//上锁
synchronized (resource2){
System.out.println("线程2占用资源2,请求资源1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//上锁
synchronized (resource1){
System.out.println("线程2成功占用资源1");
}
}
}
}).start();
}
}
运行结果
线程1占用资源1,请求资源2
线程2占用资源2,请求资源1
程序仍在运行,且永远不会自行停止,这就形成了死锁