一、死锁的简单概念
所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无其余方法作用,它们都将无法推进下去。
网友们有一个生动形象的比方:两个人面对面过独木桥,甲和乙都已经在桥上走了一段距离,即占用了桥的资源,甲如果想通过独木桥的话,乙必须退出桥面让出桥的资源,让甲通过,但是乙不服,为什么让我先退出去,我还想先过去呢,于是就僵持不下,导致谁也过不了桥,这就是死锁。其实死锁形成的关键就是:谁也不让谁,谁都不会主动地让步。
二、死锁的简单例子
例子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检查环境变量是否安装正确。