死锁分析及避免死锁

什么是死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

产生死锁

下面我们看一个简单的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;
}

针对复杂逻辑,可以拆分为多个独立方法,避免嵌套情况发生。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值