线程死锁
多个线程同时被阻塞,它们中的一个或全部都在等待某个资源被释放,由于线程无限期的被阻塞,因此程序不可能被终止。
线程死锁的4个必要条件
- 互斥:该资源在任意时刻,只能由一个线程占有
- 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放
- 不剥夺:线程已获得的资源在未使用完之前不能被其它线程强行剥夺,只有自己使用完毕后才释放资源
- 循环等待:若干个线程之间形成一种头尾相接的循环等待资源关系
案例
public class DeadLockDemo {
private static Object resource1=new Object();
private static Object resource2=new Object();
public static void main(String[] args) {
new Thread(()->{
synchronized (resource1){
System.out.println(Thread.currentThread()+"get resource1");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"waiting resource2");
synchronized (resource2){
System.out.println(Thread.currentThread()+"get resource2");
}
}
},"线程1").start();
new Thread(()->{
synchronized (resource2){
System.out.println(Thread.currentThread()+"get resource2");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"waiting resource1");
synchronized (resource1){
System.out.println(Thread.currentThread()+"get resource1");
}
}
},"线程2").start();
}
}
输出
Thread[线程1,5,main]get resource1
Thread[线程2,5,main]get resource2
Thread[线程1,5,main]waiting resource2
Thread[线程2,5,main]waiting resource1
代码解读
- 线程1通过synchronized(resource1)获取resouce1的监视锁,然后通过Thread.sleep(1000),让线程1休眠1s
- 休眠1s的目的是为了让线程2得到执行,然后获取resource2的监视锁,执行后休眠1s
- 让线程1和2休眠结束了都开始企图请求对方的资源,然后这两个线程就进入了互相等待的状态,也就产生了死锁。【符合死锁产生的4个必要条件】
如何避免死锁
避免死锁就是在资源分配时,借助于算法(比如银行家算法)对资源分配进行计算评估,使其进入安全状态。
安全状态是指系统能够按照线程推进的顺序来为每个线程分配所需资源,知道满足每个线程对资源的最大需求,使每个线程都可顺序完成。
对上述线程2进行改造
new Thread(()->{
synchronized (resource1){
System.out.println(Thread.currentThread()+"get resource1");
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread()+"waiting resource2");
synchronized (resource2){
System.out.println(Thread.currentThread()+"get resource2");
}
}
},"线程2").start();
输出
Thread[线程 1,5,main]get resource1
Thread[线程 1,5,main]waiting get resource2
Thread[线程 1,5,main]get resource2
Thread[线程 2,5,main]get resource1
Thread[线程 2,5,main]waiting get resource2
Thread[线程 2,5,main]get resource2
Process finished with exit code 0
我们分析一下上面的代码为什么避免了死锁的发生?
线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了破坏循环等待条件,因此避免了死锁。