什么是死锁
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
产生死锁
下面我们看一个简单的demo
public class DeadLockDemo {
public static void main(String[] args) {
for (int i = 1; i <= 100000; i++) {
System.out.println("第 " + i + " 次测试");
new DeadLockDemo().test();
}
}
private void test() {
User user1 = new User();
user1.currHp = 100;
User user2 = new User();
user2.currHp = 100;
Thread[] threadArray = new Thread[2];
threadArray[0] = new Thread(() -> {
user1.attkUser(user2);
});
threadArray[1] = new Thread(() -> {
user2.attkUser(user1);
});
try {
for (Thread currThread : threadArray) {
currThread.start();
}
for (Thread currThread : threadArray) {
currThread.join();
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
System.out.println("攻击完成");
}
private class User {
public int currHp;
public synchronized void subtractHp(int val) {
if (val <= 0) {
return;
}
this.currHp = this.currHp - val;
}
public synchronized void attkUser(User targetUser) {
if (null == targetUser) {
return;
}
int attkDmg = 10;
targetUser.subtractHp(attkDmg);
}
}
}
控制台输出:
第15876次测试发生了死锁
排查死锁
用jps命令查看当前运行的java进程
$ jps
80165
86628 jar
5687 Launcher
5688 DeadLockDemo
5727 Jps
用jstack 5688命令查看线程快照信息
jstack 5688
2020-09-17 15:01:46
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.261-b12 mixed mode):
"Attach Listener" #31764 daemon prio=9 os_prio=31 tid=0x00007fe1ed964000 nid=0x4233 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-31751" #31763 prio=5 os_prio=31 tid=0x00007fe1ed182000 nid=0x441b waiting for monitor entry [0x000070000a7e2000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.tinygame.herostory.DeadLockDemo$User.subtractHp(DeadLockDemo.java:47)
- waiting to lock <0x0000000797618000> (a com.tinygame.herostory.DeadLockDemo$User)
at com.tinygame.herostory.DeadLockDemo$User.attkUser(DeadLockDemo.java:59)
- locked <0x0000000797609320> (a com.tinygame.herostory.DeadLockDemo$User)
at com.tinygame.herostory.DeadLockDemo.lambda$test$1(DeadLockDemo.java:26)
at com.tinygame.herostory.DeadLockDemo$$Lambda$2/1908316405.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-31750" #31762 prio=5 os_prio=31 tid=0x00007fe1ed8a1800 nid=0xa96b waiting for monitor entry [0x000070000a6df000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.tinygame.herostory.DeadLockDemo$User.subtractHp(DeadLockDemo.java:47)
- waiting to lock <0x0000000797609320> (a com.tinygame.herostory.DeadLockDemo$User)
at com.tinygame.herostory.DeadLockDemo$User.attkUser(DeadLockDemo.java:59)
- locked <0x0000000797618000> (a com.tinygame.herostory.DeadLockDemo$User)
at com.tinygame.herostory.DeadLockDemo.lambda$test$0(DeadLockDemo.java:23)
at com.tinygame.herostory.DeadLockDemo$$Lambda$1/1025799482.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #11 daemon prio=9 os_prio=31 tid=0x00007fe1ec8c6000 nid=0x3e03 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread2" #10 daemon prio=9 os_prio=31 tid=0x00007fe1ec15a000 nid=0x3c03 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #9 daemon prio=9 os_prio=31 tid=0x00007fe1ed8a0000 nid=0x4703 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #8 daemon prio=9 os_prio=31 tid=0x00007fe1ec8bd000 nid=0x3a03 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"JDWP Command Reader" #7 daemon prio=10 os_prio=31 tid=0x00007fe1ed004000 nid=0x4903 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"JDWP Event Helper Thread" #6 daemon prio=10 os_prio=31 tid=0x00007fe1ec831000 nid=0x3903 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"JDWP Transport Listener: dt_socket" #5 daemon prio=10 os_prio=31 tid=0x00007fe1ec832000 nid=0x3807 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fe1ec81e000 nid=0x3703 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fe1ec014000 nid=0x3103 waiting for monitor entry [0x0000700009cc1000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:140)
- waiting to lock <0x00000007976009c8> (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=31 tid=0x00007fe1ec007000 nid=0x2f03 runnable [0x0000700009bbe000]
java.lang.Thread.State: RUNNABLE
at java.lang.ref.ReferenceQueue.enqueue(ReferenceQueue.java:63)
- locked <0x00000007976009c8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.Reference.tryHandlePending(Reference.java:217)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"main" #1 prio=5 os_prio=31 tid=0x00007fe1ed802800 nid=0x1a03 in Object.wait() [0x00007000095ac000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Thread.join(Thread.java:1252)
- locked <0x0000000797608f80> (a java.lang.Thread)
at java.lang.Thread.join(Thread.java:1326)
at com.tinygame.herostory.DeadLockDemo.test(DeadLockDemo.java:34)
at com.tinygame.herostory.DeadLockDemo.main(DeadLockDemo.java:11)
"VM Thread" os_prio=31 tid=0x00007fe1ec81d800 nid=0x2e03 runnable
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fe1ed000800 nid=0x2107 runnable
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fe1ed001000 nid=0x2a03 runnable
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fe1ec804800 nid=0x2c03 runnable
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fe1ec000800 nid=0x5403 runnable
"VM Periodic Task Thread" os_prio=31 tid=0x00007fe1ec8ee800 nid=0x3f03 waiting on condition
JNI global references: 2175
Found one Java-level deadlock:
=============================
"Thread-31751":
waiting to lock monitor 0x00007fe1ed998a18 (object 0x0000000797618000, a com.tinygame.herostory.DeadLockDemo$User),
which is held by "Thread-31750"
"Thread-31750":
waiting to lock monitor 0x00007fe1ed17ad68 (object 0x0000000797609320, a com.tinygame.herostory.DeadLockDemo$User),
which is held by "Thread-31751"
Java stack information for the threads listed above:
===================================================
"Thread-31751":
at com.tinygame.herostory.DeadLockDemo$User.subtractHp(DeadLockDemo.java:47)
- waiting to lock <0x0000000797618000> (a com.tinygame.herostory.DeadLockDemo$User)
at com.tinygame.herostory.DeadLockDemo$User.attkUser(DeadLockDemo.java:59)
- locked <0x0000000797609320> (a com.tinygame.herostory.DeadLockDemo$User)
at com.tinygame.herostory.DeadLockDemo.lambda$test$1(DeadLockDemo.java:26)
at com.tinygame.herostory.DeadLockDemo$$Lambda$2/1908316405.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
"Thread-31750":
at com.tinygame.herostory.DeadLockDemo$User.subtractHp(DeadLockDemo.java:47)
- waiting to lock <0x0000000797609320> (a com.tinygame.herostory.DeadLockDemo$User)
at com.tinygame.herostory.DeadLockDemo$User.attkUser(DeadLockDemo.java:59)
- locked <0x0000000797618000> (a com.tinygame.herostory.DeadLockDemo$User)
at com.tinygame.herostory.DeadLockDemo.lambda$test$0(DeadLockDemo.java:23)
at com.tinygame.herostory.DeadLockDemo$$Lambda$1/1025799482.run(Unknown Source)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
结果一目了然
避免死锁
上面demo产生死锁的原因是因为使用了嵌套锁,因为逻辑简单,可以将User类的subtractHp、attkUser方法合并。
public synchronized void attkUser(User targetUser) {
if (null == targetUser) {
return;
}
int attkDmg = 10;
targetUser.currHp = targetUser.currHp - attkDmg;
}
针对复杂逻辑,可以拆分为多个独立方法,避免嵌套情况发生。