Java并发-死锁
- 2020 年 2 月 18 日
- 筆記
一、死锁的简单概念
所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无其余方法作用,它们都将无法推进下去。 网友们有一个生动形象的比方:两个人面对面过独木桥,甲和乙都已经在桥上走了一段距离,即占用了桥的资源,甲如果想通过独木桥的话,乙必须退出桥面让出桥的资源,让甲通过,但是乙不服,为什么让我先退出去,我还想先过去呢,于是就僵持不下,导致谁也过不了桥,这就是死锁。其实死锁形成的关键就是:谁也不让谁,谁都不会主动地让步。
二、死锁的简单例子
例子1:
//类1,d1方法为需要获得类1以及类2对象的方法 class DeadLock { private OtherService otherService; private static final Object LOCK = new Object(); public DeadLock(OtherService otherService){ this.otherService = otherService; } public void d1(){ synchronized (LOCK){ System.out.println("d1==========="); otherService.o2(); } } public void d2(){ synchronized (LOCK){ System.out.println("d2==========="); } } } //类1,o1方法为需要获得类1以及类2对象的方法 class OtherService { private DeadLock deadLock; private static final Object LOCK = new Object(); public void o1() { synchronized (LOCK){ System.out.println("o1==========="); deadLock.d2(); } } public void o2() { synchronized (LOCK){ System.out.println("o2==========="); } } public void setDeadLock(DeadLock deadLock) { this.deadLock = deadLock; } } public class DeadLockTest { public static void main(String[] args) { OtherService otherService = new OtherService(); DeadLock deadLock = new DeadLock(otherService); otherService.setDeadLock(deadLock); /** * cmd通过jps命令查看该main线程的端口号 * 再通过jstack + 上面查到的端口号的命令查看栈情况 * 可以分析出来死锁情况:是否存在死锁 */ new Thread(()->{ while (true){ deadLock.d1(); } }).start(); new Thread(()->{ while (true){ otherService.o1(); } }).start(); } }
上述代码描述了Java中死锁最简单的情况,一个线程Thread-0持有锁L0并且申请获得锁L1,而另一个线程Thread-1持有锁L1并且申请获得锁L0,因为默认的锁申请操作都是阻塞的,所以线程Thrad-0和Thread-1永远被阻塞了。导致了死锁。 虽然这种僵持情况由于线程程序运行时间可能错开,而不在程序运行的开始马上发生,但是这种结构的程序,若无其他代码进行死锁去除保障,那么死锁现象一定会发生。
例子2:
public class DeadLock_join { public static void main(String[] args) { System.out.println("main线程开始运行"); try { Thread.currentThread().join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("如果打印出这段话则证明没有死锁"); } }
我们可以查看控制台观察到语句:如果打印出这段话则证明没有死锁永远得不到执行。这也是死锁的一个典型例子: 使用自己的线程对象作为同步锁,调用join方法,那么会造成死锁。
三、死锁的CMD查阅操作演示:
- cmd通过jps命令查看该main线程的端口号 Win+R,输入cmd,即可保证打开CMD端口。输入jps,根据main方法所在类的名字,找到线程的端口号。

- 再通过jstack + 上面查到的端口号的命令查看栈情况

1)可以看到线程Thread-0以及Thread-1线程发生了阻塞情况,即图中最后一行的Found 1 deadlock
提示所示。 2) -locked
则表示线程目前锁占据的锁ID,而waiting to lock
后加ID代表被当前线程锁等待的,其他线程占据的未释的锁。
我们发现线程Thread-0
锁占据的锁,正是Thread-1
锁等待的,而Thread-0
等待的锁恰好是Thread-1
锁占据的锁,除非线程等待的锁被其他线程释放了,那么当前线程才会释放自己的锁,那么其他线程才有机会获得当前线程的锁。恰恰因为线程的不主动让步,形成了死锁,2个线程”卡住了“。
注意事项: 很多人可能在CMD上输入jps/jstack等命令却不被系统识别,一般都是JDK环境在Win系统安装不正确导致,建议重新j检查环境变量是否安装正确。