什么是死锁
- 死锁: 指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁
- 死锁线程:上述永远互相等待的线程称为死锁线程
死锁模拟
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()不当的时候,极有可能产生死锁,仍然需要通过堆栈信息进行排查才能定位和解决死锁问题