1.什么是死锁?
死锁: 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
2.死锁产生的原因
1.因为系统资源不足。
2.进程运行推进的顺序不合适。
3.资源分配不当。
产生死锁的条件有四个:
1.互斥条件:所谓互斥就是进程在某一时间内独占资源。
2.请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。
4.循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
3.图解死锁产生的过程
1)有两个人——线程A、线程B。有两个房间——房间A、房间B。每个房间上有且仅有一把锁,此处可以把锁理解为进入房间的令牌
2)线程A拿到了a锁并且成功进入了A房间
3)线程B拿到了b锁并且成功进入了B房间
4)线程A拿着a锁想要进入B房间,与此同时线程B拿着b锁想要进入A房间
那么问题来了,线程A想进B房间需要拿到b锁,而b锁在线程B手中.同样,线程B想进A房间需要拿到a锁,而a锁在线程A手中
这样如果在外界没有干预的情况下 A会一直试图进入B房间却拿不到b锁,B也会一直试图进入A房间拿不到a锁 就这样一直耗下去 于是就产生了死锁现象
4.代码模拟上图中死锁现象
package com.lzh;
public class DeadLock implements Runnable{
private Object lockA = new Object();//A锁
private Object lockB = new Object();//B锁
private static Boolean flag = true;
public void run() {
if (flag){
comeRoom1();//A2.最开始flag是true A进入A房间
}else {
comeRoom2();//B4.线程B进入B房间
}
}
public void comeRoom1() {
synchronized (lockA){
System.out.println("线程"+Thread.currentThread().getName()+"进入了房间1");
try {
Thread.sleep(200);//A3.让线程A在A房间等待0.2s(给线程B进入B房间的时间 产生同一时刻A在A房间、B在B房间的情景)
} catch (InterruptedException e) {
e.printStackTrace();
}
comeRoom2();//A4.让线程A在持有A锁的情况下进入房间B
}
}
public void comeRoom2() {
synchronized (lockB){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程"+Thread.currentThread().getName()+"进入了房间2");
comeRoom1();//B5.让线程B在持有B锁的情况下进入房间A
}
}
public static void main(String[] args) {
DeadLock deadLock2 = new DeadLock();
Thread t1 = new Thread(deadLock2,"A");//线程A
Thread t2 = new Thread(deadLock2,"B");//线程B
t1.start();//A1.开始执行线程A 让A进入A房间
try {
Thread.sleep(200);//B1.先让主线程等待0.2s 确保A先进入了A房间
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = false;//B2.flag改成false 帮助B进入B房间
t2.start();//B3.线程B开启
}
}
代码中注释仅供帮助理解 并不代表真实的线程执行顺序
运行结果如下:产生死锁
5.死锁检测
5.1 使用jconsole
首先进入你的jdk安装路径的bin目录下 我这里是C:\Program Files\Java\jdk1.8.0_171\bin
找到jconsole.exe并打开 我在本地环境下运行的类是下图红框中的com.lzh.DeadLock 点击连接
点击线程 再找到对应的线程 可以看到"已锁定"三个字 并且也标明了死锁产生的位置在(DeadLock.java:31 DeadLock.java:24)
5.2 使用jvisualvm
首先进入你的jdk安装路径的bin目录下 我这里是C:\Program Files\Java\jdk1.8.0_171\bin
找到jvisualvm.exe并打开
操作如下图
6.总结
发生死锁的原因主要由于:
线程之间交错执行
解决:以固定的顺序加锁
执行某方法时就需要持有锁,且不释放
解决:缩减同步代码块范围,最好仅操作共享变量时才加锁
永久等待
解决:使用tryLock()定时锁,超过时限则返回错误信息