【JVM】面试题之死锁及问题是怎么定位

前言

之前面试的时候被问到死锁这块的问题,借着最近学习jvm来总结下死锁相关的知识。如果有地方写的不到位的地方,麻烦读者及时提出,放在评论区,我这边也好及时改正。

回顾

所谓,温故而知新,首先回顾下,我们之前学过的线程的状态以及死锁产生的条件。

线程的状态在Java中线程的状态一共被分成6种:
<ignore_js_op>
初始态(NEW)
创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态。
运行态(RUNNABLE),在Java中,运行态包括 就绪态 和 运行态。
就绪态
该状态下的线程已经获得执行所需的所有资源,只要CPU分配执行权就能运 行。 所有就绪态的线程存放在就绪队列中。
运行态
获得CPU执行权,正在执行的线程。
由于一个CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一条 运行态的线程。
阻塞态(BLOCKED)
当一条正在执行的线程请求某一资源失败时,就会进入阻塞态。
而在Java中,阻塞态专指请求锁失败时进入的状态。
由一个阻塞队列存放所有阻塞态的线程。
处于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待执 行。
等待态(WAITING)
当前线程中调用wait、join、park函数时,当前线程就会进入等待态。
也有一个等待队列存放所有等待态的线程。
线程处于等待态表示它需要等待其他线程的指示才能继续运行。
进入等待态的线程会释放CPU执行权,并释放资源(如:锁)
超时等待态(TIMED_WAITING)
当运行中的线程调用sleep(time)、wait、join、parkNanos、parkUntil时,就 会进入该状态;
它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其 他线程唤醒;
进入该状态后释放CPU执行权 和 占有的资源。
与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。
终止态(TERMINATED)
线程执行结束后的状态。
死锁产生的条件
互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源
请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放
不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放
环路等待条件:是指进程发生死锁后,必然存在一个进程–资源之间的环形链死锁问题
构造死锁
编写代码,启动2个线程,Thread1拿到了obj1锁,准备去拿obj2锁时,obj2已经被 Thread2锁定,所以发送了死锁。

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class TestDeadLock {
 
     private static Object obj1 = new Object();
     private static Object obj2 = new Object();
 
     public static void main(String[] args) {
         new Thread( new Thread1()).start();
         new Thread( new Thread2()).start();
     }
 
     private static class Thread1 implements Runnable {
         @Override
         public void run() {
             synchronized (obj1) {
                 System.out.println( "Thread1 拿到 obj1 的锁" );
                 try {
                     //停顿2秒的意义在于,让thread2线程拿到obj2的锁
                     Thread.sleep(2000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
 
                 synchronized (obj2) {
                     System.out.println( "Thread1 拿到 obj2 的锁" );
                 }
             }
 
         }
     }
 
     private static class Thread2 implements Runnable {
         @Override
         public void run() {
             synchronized (obj2) {
                 System.out.println( "Thread2 拿到 obj2 的锁" );
                 try {
                     //停顿2秒的意义在于,让thread1线程拿到obj1的锁
                     Thread.sleep(2000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
 
                 synchronized (obj1) {
                     System.out.println( "Thread2 拿到 obj1 的锁" );
                 }
             }
         }
     }
 
 
}



[Java]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
[root @localhost test]# javac TestDeadLock.java
[root @localhost test]# ll
total 27692
drwxr-xr-x. 9 root root      220 Jan  3 23 : 22 apache-tomcat- 7.0 . 99
-rw-r--r--. 1 root root  9587605 Dec 11 21 : 44 apache-tomcat- 7.0 . 99 .tar.gz
-rw-------. 1 root root 18737999 Jan  4 16 : 52 dump.dat
-rw-r--r--. 1 root root      433 Jan  3 23 : 23 Main. class
-rw-r--r--. 1 root root      135 Jan  3 23 : 23 Main.java
-rw-r--r--. 1 root root      184 Jan  5 10 : 08 TestDeadLock$ 1 . class
-rw-r--r--. 1 root root      843 Jan  5 10 : 08 TestDeadLock. class
-rw-r--r--. 1 root root     1547 Jan  5 10 : 02 TestDeadLock.java
-rw-r--r--. 1 root root     1066 Jan  5 10 : 08 TestDeadLock$Thread1. class
-rw-r--r--. 1 root root     1066 Jan  5 10 : 08 TestDeadLock$Thread2. class
[root @localhost test]# java TestDeadLock
Thread1 拿到 obj1 的锁
Thread2 拿到 obj2 的锁
#这里发生了死锁,程序一直将等待下去


jstack命令分析

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
[root@localhost ~] # jstack 13399
2020-01-05 10:09:42
Full thread dump OpenJDK 64-Bit Server VM (25.232-b09 mixed mode):
 
"Attach Listener" #11 daemon prio=9 os_prio=0 tid=0x00007f5bf0001000 nid=0x3477 waiting on condition [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"DestroyJavaVM" #10 prio=5 os_prio=0 tid=0x00007f5c1804b800 nid=0x3458 waiting on condition [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"Thread-1" #9 prio=5 os_prio=0 tid=0x00007f5c18149000 nid=0x3462 waiting for monitor entry [0x00007f5c1cac7000]
    java.lang.Thread.State: BLOCKED (on object monitor)
     at TestDeadLock$Thread2.run(TestDeadLock.java:45)
     - waiting to lock <0x00000000e3466bd0> (a java.lang.Object)
     - locked <0x00000000e3466be0> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:748)
 
"Thread-0" #8 prio=5 os_prio=0 tid=0x00007f5c18147000 nid=0x3461 waiting for monitor entry [0x00007f5c1cbc8000]
    java.lang.Thread.State: BLOCKED (on object monitor)
     at TestDeadLock$Thread1.run(TestDeadLock.java:25)
     - waiting to lock <0x00000000e3466be0> (a java.lang.Object)
     - locked <0x00000000e3466bd0> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:748)
 
"Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007f5c18117800 nid=0x345f runnable [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007f5c18114800 nid=0x345e waiting on condition [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007f5c18105800 nid=0x345d waiting on condition [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007f5c18103800 nid=0x345c runnable [0x0000000000000000]
    java.lang.Thread.State: RUNNABLE
 
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007f5c180da000 nid=0x345b in Object.wait() [0x00007f5c1d62a000]
    java.lang.Thread.State: WAITING (on object monitor)
     at java.lang.Object.wait(Native Method)
     - waiting on <0x00000000e3408ed8> (a java.lang.ref.ReferenceQueue$Lock)
     at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
     - locked <0x00000000e3408ed8> (a java.lang.ref.ReferenceQueue$Lock)
     at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
     at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
 
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007f5c180d5000 nid=0x345a in Object.wait() [0x00007f5c1d72b000]
    java.lang.Thread.State: WAITING (on object monitor)
     at java.lang.Object.wait(Native Method)
     - waiting on <0x00000000e3406c00> (a java.lang.ref.Reference$Lock)
     at java.lang.Object.wait(Object.java:502)
     at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
     - locked <0x00000000e3406c00> (a java.lang.ref.Reference$Lock)
     at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
 
"VM Thread" os_prio=0 tid=0x00007f5c180cb800 nid=0x3459 runnable
 
"VM Periodic Task Thread" os_prio=0 tid=0x00007f5c1811a000 nid=0x3460 waiting on condition
 
JNI global references: 5
 
 
Found one Java-level deadlock:
=============================
"Thread-1" :
   waiting to lock monitor 0x00007f5bfc0062c8 (object 0x00000000e3466bd0, a java.lang.Object),
   which is held by "Thread-0"
"Thread-0" :
   waiting to lock monitor 0x00007f5bfc004e28 (object 0x00000000e3466be0, a java.lang.Object),
   which is held by "Thread-1"
 
Java stack information for the threads listed above:
===================================================
"Thread-1" :
     at TestDeadLock$Thread2.run(TestDeadLock.java:45)
     - waiting to lock <0x00000000e3466bd0> (a java.lang.Object)
     - locked <0x00000000e3466be0> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:748)
"Thread-0" :
     at TestDeadLock$Thread1.run(TestDeadLock.java:25)
     - waiting to lock <0x00000000e3466be0> (a java.lang.Object)
     - locked <0x00000000e3466bd0> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:748)


在输出的信息中,已经看到,发现了1个死锁,关键看这个:

[JavaScript]  纯文本查看 复制代码
?
01
02
03
04
05
06
07
08
09
10
"Thread-1" :
     at TestDeadLock$Thread2.run(TestDeadLock.java:45)
     - waiting to lock <0x00000000e3466bd0> (a java.lang.Object)
     - locked <0x00000000e3466be0> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:748)
"Thread-0" :
     at TestDeadLock$Thread1.run(TestDeadLock.java:25)
     - waiting to lock <0x00000000e3466be0> (a java.lang.Object)
     - locked <0x00000000e3466bd0> (a java.lang.Object)
     at java.lang.Thread.run(Thread.java:748)


可以清晰的看到:

Thread2获取了 <0x00000000e3466be0> 的锁,等待获取 <0x00000000e3466bd0> 这个锁
Thread1获取了 <0x00000000e3466bd0> 的锁,等待获取 <0x00000000e3466be0> 这个锁
由此可见,发生了死锁。

更多java学习资料可关注:itheimaGZ获取

猜你喜欢

转载自www.cnblogs.com/zhuxiaopijingjing/p/12289928.html
今日推荐