浅谈死锁以及判断死锁的方法
引言
我们在并发情况下见过很多种锁,synchronized,ReentrantLock 等等,这些锁是为了保证线程安全,使线程同步的锁,与今天所要学习的死锁并不相同,死锁并不是一种锁,而是一种现象。
官方定义:死锁就是在两个或两个以上线程都想获得对方所持有的资源,从而相互等待的这么一种状态。
*************************************************举个栗子*************************************************
你正在面试,面试官让你给他讲一讲什么是死锁,然后你告诉面试官:“你给我发 offer 我就讲讲什么是死锁。”面试官说:“你给我讲讲死锁,我就给你发 offer。”这就进入了互相等待的状态,面试官想获得你对死锁的理解,你又想获得面试官的 offer 这就是两个线程都想获得对方所持有的资源,从而互相等待。
代码示例
程序中有两个线程
线程 t1 需要先获得锁 lockA 再获得锁 lockB
线程 t2 需要先获得锁 lockB 再获得锁 lockA
我们可以预料的是,当 t1,t2 线程启动之后,t1 线程获得了 lockA,t2 线程获得了 lockB,这时候 t1 线程再想获得 lockB 就会进入到 blocked 状态,t2 线程想获得 lockA 也会进入到 blocked 状态,这样两个线程就会互相等待,造成死锁。
public class Demo01 {public static void main(String[] args) {DeadLock deadLock = new DeadLock();Thread t1 = new Thread(() -> {try {deadLock.add();} catch (InterruptedException e) {throw new RuntimeException(e);}}, "t1线程");Thread t2 = new Thread(() -> {deadLock.dec();}, "t2线程");t1.start();t2.start();}
}class DeadLock {private final Object lockA = new Object();private final Object lockB = new Object();public void add() throws InterruptedException {synchronized (lockA) { // 获取lockA/*** 线程休眠* 为了让其他线程尽快获得lockB* 否则当前线程执行太快会得到lockA,lockB就不会死锁*/Thread.sleep(1000);synchronized (lockB) { // 又获取到了lockBSystem.out.println("add()");}}}public void dec() {synchronized (lockB) {synchronized (lockA) {System.out.println("dec()");}}}
}
判断死锁
程序出现死锁之后就会,进入等待状态,不会向下运行,那么难道只要程序出现了等待这种状态就是死锁了吗?
其实不一定,我们可以使用两个命令来观测
- 使用
jps -l
命令,显示本地所有JVM进程,查找当前JVM进程的进程号
- 通过
jstack
命令,显示当前JVM虚拟机的栈信息,查找产生死锁的线程
这就是确定程序出现死锁了
避免死锁的方式
- 持有的锁不要超过 1 把
- 按相同顺序获得锁,这样没有锁的线程就会进入 blocked 状态