面试的时候会问到死锁问题,下面我就编写一个死锁程序来进行分析。
要想编写一个100%死锁的程序有点难度,大多数人编写的死锁程序是使用Thread.sleep函数来实现,但大家都明白,其实由于最终的线程调度取决于OS调度器,而它如何执行调度是不确定的,所以没有100%死锁的程序,只能尽可能死锁。
下面这个例子就是一个在大多数运行时死锁的程序。
package com.iydsj.server.hole;
/**
* 尽可能死锁的程序
*/
public class DeadLock implements Runnable{
public static final Object AAA = new Object();
public static final Object BBB = new Object();
boolean flag;
public DeadLock(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (AAA) {
System.out.println("if lock AAA");
synchronized (BBB) {
try {
Thread.sleep(1000);
}catch (Exception ex){
System.out.println("error");
}
System.out.println("if lock BBB");
}
}
}
else {
synchronized (BBB) {
System.out.println("else lock BBB");
synchronized (AAA) {
try {
Thread.sleep(1000);
}catch (Exception ex){
System.out.println("error");
}
System.out.println("else lock AAA");
}
}
}
}
public static void main(String[] args) {
DeadLock A = new DeadLock(true);
DeadLock B = new DeadLock(false);
new Thread(A).start();
new Thread(B).start();
}
}
- 死锁检查
当你怀疑出现死锁的时候,可以通过一系列的工具来检查是否出现了死锁。
- 首先通过命令jcmd找到你运行的程序的PID
[root@iZ23tgthnkpZ ~]# jcmd
21009 org.apache.catalina.startup.Bootstrap start
17133 com.aliyun.tianji.cloudmonitor.Application
5935 org.apache.catalina.startup.Bootstrap start
6284 org.apache.catalina.startup.Bootstrap start
13612 sun.tools.jcmd.JCmd
13550 com/i/DeadLock
6184 org.apache.catalina.startup.Bootstrap start
6442 org.apache.catalina.startup.Bootstrap start
31880 org.apache.catalina.startup.Bootstrap start
这里我的程序是com.i.DeadLock.java,它的PID是13550。
- 接着通过命令jstack
查看堆栈信息
[root@iZ23tgthnkpZ ~]# jstack 13550
2018-10-18 10:45:44
Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.79-b02 mixed mode):
"Attach Listener" daemon prio=10 tid=0x00007f1278001000 nid=0x3548 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" prio=10 tid=0x00007f12b0008800 nid=0x34ef waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" prio=10 tid=0x00007f12b00b2000 nid=0x34fd waiting for monitor entry [0x00007f129fefd000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.i.DeadLock.run(DeadLock.java:35)
- waiting to lock <0x00000007d704c498> (a java.lang.Object)
- locked <0x00000007d704c4a8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" prio=10 tid=0x00007f12b00b0000 nid=0x34fc waiting for monitor entry [0x00007f129fffe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.i.DeadLock.run(DeadLock.java:22)
- waiting to lock <0x00000007d704c4a8> (a java.lang.Object)
- locked <0x00000007d704c498> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Service Thread" daemon prio=10 tid=0x00007f12b0096800 nid=0x34fa runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" daemon prio=10 tid=0x00007f12b0094800 nid=0x34f9 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" daemon prio=10 tid=0x00007f12b0091800 nid=0x34f8 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" daemon prio=10 tid=0x00007f12b008f000 nid=0x34f7 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=10 tid=0x00007f12b006e800 nid=0x34f6 in Object.wait() [0x00007f12ac6b3000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007d7004858> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x00000007d7004858> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
"Reference Handler" daemon prio=10 tid=0x00007f12b006c800 nid=0x34f5 in Object.wait() [0x00007f12ac7b4000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000007d7004470> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
- locked <0x00000007d7004470> (a java.lang.ref.Reference$Lock)
"VM Thread" prio=10 tid=0x00007f12b0068000 nid=0x34f4 runnable
"GC task thread#0 (ParallelGC)" prio=10 tid=0x00007f12b001e000 nid=0x34f0 runnable
"GC task thread#1 (ParallelGC)" prio=10 tid=0x00007f12b0020000 nid=0x34f1 runnable
"GC task thread#2 (ParallelGC)" prio=10 tid=0x00007f12b0022000 nid=0x34f2 runnable
"GC task thread#3 (ParallelGC)" prio=10 tid=0x00007f12b0023800 nid=0x34f3 runnable
"VM Periodic Task Thread" prio=10 tid=0x00007f12b00a1800 nid=0x34fb waiting on condition
JNI global references: 107
Found one Java-level deadlock:
=============================
"Thread-1":
waiting to lock monitor 0x00007f12840062c8 (object 0x00000007d704c498, a java.lang.Object),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x00007f1284004ed8 (object 0x00000007d704c4a8, a java.lang.Object),
which is held by "Thread-1"
Java stack information for the threads listed above:
===================================================
"Thread-1":
at com.i.DeadLock.run(DeadLock.java:35)
- waiting to lock <0x00000007d704c498> (a java.lang.Object)
- locked <0x00000007d704c4a8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Thread-0":
at com.i.DeadLock.run(DeadLock.java:22)
- waiting to lock <0x00000007d704c4a8> (a java.lang.Object)
- locked <0x00000007d704c498> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
Found 1 deadlock.
可以看到最后一句”Found 1 deadlock”,说明程序中出现了死锁。那么究竟是哪两个线程死锁了呢?基本思路是查找锁的地址,一个线程在等待某个地址的锁,另一个获取了某个地址的锁,如下,仔细看两个地址0x00000007d704c4a8和0x00000007d704c498
"Thread-1" prio=10 tid=0x00007f12b00b2000 nid=0x34fd waiting for monitor entry [0x00007f129fefd000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.i.DeadLock.run(DeadLock.java:35)
- waiting to lock <0x00000007d704c498> (a java.lang.Object)
- locked <0x00000007d704c4a8> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" prio=10 tid=0x00007f12b00b0000 nid=0x34fc waiting for monitor entry [0x00007f129fffe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.i.DeadLock.run(DeadLock.java:22)
- waiting to lock <0x00000007d704c4a8> (a java.lang.Object)
- locked <0x00000007d704c498> (a java.lang.Object)
at java.lang.Thread.run(Thread.java:745)
- 如何在编程中避免死锁
思路如下:
- 如果可能的话,尽量避免使用多把锁
- 如果必须使用多把锁,尽量设计好锁的获取顺序