并发编程之死锁解析

在 Java 的并发编程中,有一个问题须要特别注意,那就是死锁,若是发生了死锁,基本就是重启,而重启将会丢失运行中的数据。因此,了解死锁的造成并排查死锁到预防死锁成了一个重要的问题。
1.什么是死锁
[Java] 纯文本查看 复制代码
?java

public class DeadLock {编程

public static void main(String[] args) {并发

new Thread(() -> {
  try {
     new DeadLock().resource1();
  } catch (InterruptedException e) {
  }
}
).start();

new Thread(() -> {
  try {
     new DeadLock().resource2();
  } catch (InterruptedException e) {
  }
}
).start();

}app

void resource1() throws InterruptedException {socket

synchronized ("resource1") {
  System.out.println("获取资源1");
   // 等待 1 秒让另外一个线程拿到锁
  Thread.sleep(1000);
  resource2();
}

}spa

void resource2() throws InterruptedException {.net

synchronized ("resource2") {
  System.out.println("获取资源2");
  // 等待 1 秒让另外一个线程拿到锁
  Thread.sleep(1000);
  resource1();
}

}
}线程

上面的代码中,咱们启用了两个线程,分别抢占2个资源,但这两个资源又分别被不一样的对象(字符串)锁住了。当第一个线程调用 resource1 方法,进入同步块,拿到锁,并等待 1 秒钟让另外一个线程进入 resource2 同步块,当第二个线程进入同步块后,注意:此时, 拿着 resourec1 锁的线程企图拿到 resource2 的锁,但这个时候,拿着 resource2 的线程也想去拿 resource1 的锁。因而就出现了互相僵持的状况,谁也没法拿到对方的锁,整个系统就卡死了。
2.如何检测死锁
[Java] 纯文本查看 复制代码
?code

Full thread dump Java HotSpot(TM) Client VM (25.131-b11 mixed mode):orm

"DestroyJavaVM" #11 prio=5 os_prio=0 tid=0x051fe800 nid=0x1e0c waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE

"Thread-1" #10 prio=5 os_prio=0 tid=0x18777800 nid=0x5664 waiting for monitor entry [0x18e0f000]
java.lang.Thread.State: BLOCKED (on object monitor)

at hello.DeadLock.resource1(DeadLock.java:31)
    - waiting to lock <0x07415a50> (a java.lang.String)
    at hello.DeadLock.resource2(DeadLock.java:43)
    - locked <0x0742bd18> (a java.lang.String)
    at hello.DeadLock.lambda$main$1(DeadLock.java:20)
    at hello.DeadLock$$Lambda$2/4983748.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"Thread-0" #9 prio=5 os_prio=0 tid=0x18776c00 nid=0x4dc4 waiting for monitor entry [0x18d7f000]
java.lang.Thread.State: BLOCKED (on object monitor)

at hello.DeadLock.resource2(DeadLock.java:41)
    - waiting to lock <0x0742bd18> (a java.lang.String)
    at hello.DeadLock.resource1(DeadLock.java:33)
    - locked <0x07415a50> (a java.lang.String)
    at hello.DeadLock.lambda$main$0(DeadLock.java:11)
    at hello.DeadLock$$Lambda$1/5592464.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"Service Thread" #8 daemon prio=9 os_prio=0 tid=0x186e4c00 nid=0x172c runnable [0x00000000]
java.lang.Thread.State: RUNNABLE

"C1 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x186af000 nid=0x53f8 waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x1861e800 nid=0x3928 runnable [0x18b3f000]
java.lang.Thread.State: RUNNABLE

at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.net.SocketInputStream.read(SocketInputStream.java:171)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
    at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
    at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
    - locked <0x07861da0> (a java.io.InputStreamReader)
    at java.io.InputStreamReader.read(InputStreamReader.java:184)
    at java.io.BufferedReader.fill(BufferedReader.java:161)
    at java.io.BufferedReader.readLine(BufferedReader.java:324)
    - locked <0x07861da0> (a java.io.InputStreamReader)
    at java.io.BufferedReader.readLine(BufferedReader.java:389)
    at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)

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

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

"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x17972400 nid=0x41a8 in Object.wait() [0x17cff000]
java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)
    - waiting on <0x0ca1b830> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
    - locked <0x0ca1b830> (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=0x17960000 nid=0x4ef0 in Object.wait() [0x17c6f000]
java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)
    - waiting on <0x0ca1b9d0> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:502)
    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    - locked <0x0ca1b9d0> (a java.lang.ref.Reference$Lock)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"VM Thread" os_prio=2 tid=0x1795a800 nid=0x3f54 runnable

"VM Periodic Task Thread" os_prio=2 tid=0x18739400 nid=0x4a14 waiting on condition

JNI global references: 229

// 找到一个死锁

Found one Java-level deadlock:

"Thread-1":
waiting to lock monitor 0x17978de4 (object 0x07415a50, a java.lang.String),
which is held by "Thread-0"
"Thread-0":
waiting to lock monitor 0x1797a974 (object 0x0742bd18, a java.lang.String),
which is held by "Thread-1"

Java stack information for the threads listed above:

"Thread-1":

at hello.DeadLock.resource1(DeadLock.java:31)
     // 等待 0x07415a50 锁
    - waiting to lock <0x07415a50> (a java.lang.String)
    at hello.DeadLock.resource2(DeadLock.java:43)
    // 持有 0x0742bd18
    - locked <0x0742bd18> (a java.lang.String)
    at hello.DeadLock.lambda$main$1(DeadLock.java:20)
    at hello.DeadLock$$Lambda$2/4983748.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"Thread-0":

at hello.DeadLock.resource2(DeadLock.java:41)
    // 等待 0x0742bd18 锁
    - waiting to lock <0x0742bd18> (a java.lang.String)
    at hello.DeadLock.resource1(DeadLock.java:33)
    // 持有 0x07415a50
    - locked <0x07415a50> (a java.lang.String)
    at hello.DeadLock.lambda$main$0(DeadLock.java:11)
    at hello.DeadLock$$Lambda$1/5592464.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

// 发现了一个死锁
Found 1 deadlock.

3.死锁造成的缘由
互斥条件:一个资源每次只能被一个线程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已得到的资源保持不放。
不剥夺条件:进程已得到的资源,在未使用完以前,不能强行剥夺。
循环等待条件:若干进程之间造成一种头尾相接的循环等待资源关系。

死锁是由四个必要条件致使的,因此通常来讲,只要破坏这四个必要条件中的一个条件,死锁状况就应该不会发生。若是想要打破互斥条件,咱们须要容许进程同时访问某些资源,这种方法受制于实际场景,不太容易实现条件;打破不可抢占条件,这样须要容许进程强行从占有者那里夺取某些资源,或者简单一点理解,占有资源的进程不能再申请占有其余资源,必须释放手上的资源以后才能发起申请,这个其实也很难找到适用场景;进程在运行前申请获得全部的资源,不然该进程不能进入准备执行状态。这个方法看似有点用处,可是它的缺点是可能致使资源利用率和进程并发性下降;避免出现资源申请环路,即对资源事先分类编号,按号分配。这种方式能够有效提升资源的利用率和系统吞吐量,可是增长了系统开销,增大了进程对资源的占用时间。

相关文章
相关标签/搜索