1. 什么是死锁?
死锁是一种特定的程序状态,主要是由于循环依赖导致彼此一直处于等待中,而使得程序陷入僵局,相当尴尬。死锁不仅仅发生在线程之间,而对于资源独占的进程之间同样可能出现死锁。通常来说,我们所说的死锁,是指两个或多个线程之间,由于互相持有对方所需要的锁,进而产生永久阻塞的情况。举个栗子,线程1手里有锁A,它想要获得锁B,与此同时,线程2手里有锁B,它想要获得锁A,相持不下,那么这两个线程将永远地等待下去。
一句话,计算机资源有限,大家互相占着资源而不放手,而且还想要更多,导致计算机say no.
2. 定位死锁
最常见的方式是利用jstack等工具获取线程,然后定位互相之间的依赖关系,然后找到死锁。如果是比较明显的死锁,是可以直接定位到了,类似JConsole,甚至于通过图形界面进行有限的死锁检测。
下面这段代码执行后就会产生死锁
/**
* synchronized 死锁
* 有两个线程A和B,线程A先执行m1方法,在m1方法中停顿5s,当启动线程A 2s 后启动线程B
* 线程B执行m2方法
* 此时就形成了死锁 A线程需要锁 lockb , B线程需要locka
*/
class SynchronizedDeadLockDemo {
private Object locka = new Object();
private Object lockb = new Object();
public void dead1() throws InterruptedException {
synchronized (locka) {
System.out.println(Thread.currentThread().getName() + "\t[占有锁locka]");
TimeUnit.SECONDS.sleep(5);
synchronized (lockb) {
System.out.println(Thread.currentThread().getName() + "\t[占有锁lockb]");
}
System.out.println(Thread.currentThread().getName() + "\t[释放锁lockb]");
}
System.out.println(Thread.currentThread().getName() + "\t[释放锁locka]");
}
public void dead2() {
synchronized (lockb) {
System.out.println(Thread.currentThread().getName() + "\t[占有锁lockb]");
synchronized (locka) {
System.out.println(Thread.currentThread().getName() + "\t[占有锁locka]");
}
System.out.println(Thread.currentThread().getName() + "\t[释放锁locka]");
}
System.out.println(Thread.currentThread().getName() + "\t[释放锁lockb]");
}
}
/**
* @author shenguangyang
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
SynchronizedDeadLockDemo lockDemo = new SynchronizedDeadLockDemo();
new Thread(() -> {
try {
lockDemo.dead1();
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
TimeUnit.SECONDS.sleep(2);
new Thread(() -> {
lockDemo.dead2();
},"B").start();
}
}
2.1. 使用 jps + jstack
第一:在windons命令窗口,使用 jps -l 【不会使用jps请自行查询资料】
第二:使用 jstack -l 7068 【不会使用jstack请自行查询资料】
2.2. 使用jconsole
在window打开 JConsole,JConsole是一个图形化的监控工具!
在windons命令窗口 ,输出 JConsole
2.3. 使用Java Visual VM
在window打开 jvisualvm,jvisualvm是一个图形化的监控工具!
在windons命令窗口 ,输出 jvisualvm
3. 死锁的诞生
- 互斥条件 资源独占,若其他进程使用,必须等待;
- 长期持有 死锁一直存在,自己不会释放,也不能被释放;
- 循环依赖关系 彼此等待,形成进程循环等待环。
4. 如何防患?
- 尽量避免使用多个锁(如果有可能的话)。
- 规范的使用多个锁,并设计好锁的获取顺序。
- 随用随放。即是,手里有锁,如果还要获得别的锁,必须释放全部资源才能各取所需。
- 规范好循环等待条件。比如,使用超时循环等待,提高程序可控性