DeadLock死锁的出现和检测-模拟死锁

什么是死锁

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

死锁模拟

package com.milla.study.netbase.expert.jvm;

/**
 * @Description: <模拟死锁>
 * @Author: MILLA
 * @CreateDate: 2020/4/23 15:16
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/4/23 15:16
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public class DeadLockDemo {
    public static String obj1 = "obj1";
    public static String obj2 = "obj2";

    public static void main(String[] args) {
        // 线程a 先获取obj1锁,然后等待3秒之后获取obj2锁,此时obj2锁已经被线程b占有,线程a就一直等待线程b释放obj2锁
        // 线程b 先获取obj2锁,然后等待3秒之后获取obj1锁,此时obj1锁已经被线程a占有,线程b就一直等待线程a释放obj2锁
        // 最终线程a和线程b陷入一个先有蛋还是先有鸡的循环等待问题,此时产生死锁
        Thread a = new Thread(new Lock1());
        Thread b = new Thread(new Lock2());
        a.start();
        b.start();
    }
}

class Lock1 implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("Lock1 running");
            while (true) {
                synchronized (DeadLockDemo.obj1) {
                    System.out.println("Lock1 lock obj1");
                    //获取obj1后先等一会儿,让Lock2有足够的时间锁住obj2
                    Thread.sleep(3000);
                    synchronized (DeadLockDemo.obj2) {
                        System.out.println("Lock1 lock obj2");
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

class Lock2 implements Runnable {
    @Override
    public void run() {
        try {
            System.out.println("Lock2 running");
            while (true) {
                synchronized (DeadLockDemo.obj2) {
                    System.out.println("Lock2 lock obj2");
                    Thread.sleep(3000);
                    synchronized (DeadLockDemo.obj1) {
                        System.out.println("Lock2 lock obj1");
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

因为资源竞争导致死锁

 查看进程情况

# 查看进程号
[root@localhost ~]# jcmd -l
15808 com.milla.study.netbase.expert.jvm.DeadLockDemo
21312 sun.tools.jcmd.JCmd -l

# 查看进程的堆栈信息
[root@localhost ~]# jstack 15808
2020-08-04 18:24:02
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode):

"DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x0000000003477000 nid=0x265c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Thread-1" #14 prio=5 os_prio=0 tid=0x0000000023705000 nid=0x2f58 waiting for monitor entry [0x000000002428e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
       # 可以定位在发生死锁的大致位置
        at com.milla.study.netbase.expert.jvm.Lock2.run(DeadLockDemo.java:59)
        - waiting to lock <0x000000074121ec30> (a java.lang.String)
        - locked <0x000000074121ec60> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)

"Thread-0" #13 prio=5 os_prio=0 tid=0x0000000023702000 nid=0x459c waiting for monitor entry [0x000000002418f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.milla.study.netbase.expert.jvm.Lock1.run(DeadLockDemo.java:39)
        - waiting to lock <0x000000074121ec60> (a java.lang.String)
        - locked <0x000000074121ec30> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)

"Service Thread" #12 daemon prio=9 os_prio=0 tid=0x0000000023607800 nid=0x18a4 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #11 daemon prio=9 os_prio=2 tid=0x00000000235dc800 nid=0x1a70 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #10 daemon prio=9 os_prio=2 tid=0x0000000023584800 nid=0x36f0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #9 daemon prio=9 os_prio=2 tid=0x0000000023584000 nid=0x1bf0 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Command Reader" #8 daemon prio=10 os_prio=0 tid=0x0000000021771000 nid=0xc78 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Event Helper Thread" #7 daemon prio=10 os_prio=0 tid=0x000000002176d800 nid=0x1164 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"JDWP Transport Listener: dt_socket" #6 daemon prio=10 os_prio=0 tid=0x0000000021760000 nid=0x4ec4 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x0000000022ad3000 nid=0x1e1c waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x0000000022ad2800 nid=0x4c10 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000002171f000 nid=0x2cb0 in Object.wait() [0x0000000022a8e000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000740c08ec8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
        - locked <0x0000000740c08ec8> (a java.lang.ref.ReferenceQueue$Lock)
        at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x0000000003567000 nid=0x4284 in Object.wait() [0x000000002298e000]
   java.lang.Thread.State: WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x0000000740c06b68> (a java.lang.ref.Reference$Lock)
        at java.lang.Object.wait(Object.java:502)
        at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
        - locked <0x0000000740c06b68> (a java.lang.ref.Reference$Lock)
        at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x00000000216f7800 nid=0x1dd0 runnable

"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x000000000348c800 nid=0x50e8 runnable

"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x000000000348e800 nid=0x4054 runnable

"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x0000000003490000 nid=0x2384 runnable

"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x0000000003491800 nid=0x1f00 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x00000000236a5000 nid=0x19d8 waiting on condition

JNI global references: 1702


Found one Java-level deadlock: # 找到了一个死锁
=============================
"Thread-1": # 
  waiting to lock monitor 0x00000000216fe0a8 (object 0x000000074121ec30, a java.lang.String),
  which is held by "Thread-0"
"Thread-0":
  waiting to lock monitor 0x00000000216fb768 (object 0x000000074121ec60, a java.lang.String),
  which is held by "Thread-1"

Java stack information for the threads listed above:
===================================================
"Thread-1": # 线程1已经获取0x000000074121ec60锁,在等待获取0x000000074121ec30锁
        at com.milla.study.netbase.expert.jvm.Lock2.run(DeadLockDemo.java:59)
        - waiting to lock <0x000000074121ec30> (a java.lang.String)
        - locked <0x000000074121ec60> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)
"Thread-0": # 于此同时 线程0已经获取了0x000000074121ec30锁,在等待0x000000074121ec60锁
        at com.milla.study.netbase.expert.jvm.Lock1.run(DeadLockDemo.java:39)
        - waiting to lock <0x000000074121ec60> (a java.lang.String)
        - locked <0x000000074121ec30> (a java.lang.String)
        at java.lang.Thread.run(Thread.java:748)

Found 1 deadlock.
# 以上两个线程,在都不放弃之前锁的情况下,就这么一直等待,就产生了死锁

 此时就发现了死锁,基本能定位发生死锁代码的位置,方便程序员进行代码定位

因为代码原因导致的死锁 

package com.milla.study.netbase.expert.jvm;

/**
 * @Description: <消费代码写的有问题带来的死锁>
 * @Author: MILLA
 * @CreateDate: 2020/4/23 15:16
 * @UpdateUser: MILLA
 * @UpdateDate: 2020/4/23 15:16
 * @UpdateRemark: <>
 * @Version: 1.0
 */
public class ConsumerProductDeadLock {
    /**
     * 生产货物
     */
    public static Object product = null;

    /**
     * 会导致程序永久等待的wait/notify
     */
    public void waitNotifyDeadLockTest() throws Exception {
        // 启动消费者线程
        new Thread(() -> {
            //没有货物可消费需要进行等待
            if (product == null) {
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                synchronized (this) {
                    try {
                        System.out.println("1、进入等待,线程ID为: " + Thread.currentThread().getId());
                        this.wait(); // 多次查看
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
            System.out.println("2、买到获取,回家");
        }).start();
        // 此时休眠的时间比没有货物休眠的时间端,导致下面的代码先运行,已经执行了this.notifyAll();
        // 致使wait的线程永远也不会被唤醒导致死锁
        Thread.sleep(3000L);
        product = new Object();
        synchronized (this) {
            this.notifyAll();
            System.out.println("3、通知消费者");
        }
    }

    public static void main(String[] args) throws Exception {
        new ConsumerProductDeadLock().waitNotifyDeadLockTest();
    }
}

当使用 wait()和notifyAll()不当的时候,极有可能产生死锁,仍然需要通过堆栈信息进行排查才能定位和解决死锁问题

 

你可能感兴趣的:(高性能编程,Lock,锁,java,问题排查,jvm)