preface

In Concurrent programming in Java, there is a particular issue to be aware of, and that is deadlocks. If a deadlock occurs, it is basically a restart, which will lose running data. Therefore, understanding the formation of deadlocks and troubleshooting deadlocks to prevent deadlocks has become an important issue.

The steps we take to understand anything are: What, how, why, why not.

1. What is a deadlock?

Let’s go ahead and write some code:

package hello;

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();
  }

  void resource1(a) throws InterruptedException {

    synchronized ("resource1") {
      System.out.println("Obtaining Resource 1");
       // Wait 1 second for another thread to take the lock
      Thread.sleep(1000); resource2(); }}void resource2(a) throws InterruptedException {
    synchronized ("resource2") {
      System.out.println("Access to Resources 2");
      // Wait 1 second for another thread to take the lock
      Thread.sleep(1000); resource1(); }}}Copy the code

In the above code, we enable two threads to preempt two resources, which are locked by different objects (strings). When the first thread calls the resource1 method, it enters the block, takes the lock, and waits 1 second for another thread to enter the resource2 block. After the second thread enters the block, note: At this point, the thread holding the resourec1 lock attempts to acquire the resource2 lock, but the thread holding the Resource2 lock also attempts to acquire the resource1 lock. So there was a standoff, no one could get the lock on the other side, and the whole system was stuck.

This situation is called a deadlock.

For example, if the code we are writing is deliberately created deadlock, we can find out, what if the online environment, if our system is stuck, how do we know which part of the code is the problem, whether there is a deadlock problem. That is how to detect deadlocks.

2. How to detect deadlocks?

Because deadlocks are extremely difficult to detect manually, the JDK provides commands to detect the state of a Java process’s central thread and check for deadlocks. What about the command? JPS, used to view the Java program process number, of course, in Linux can also be obtained by other ways, jstack process number command can allow the corresponding process stack information, and find deadlocks.

Let’s use this command on Windows for the program we just did.

C:\Users\stateis0>jps
11060
2084 Launcher
10712 RemoteMavenServer
18040 Jps
11820 DeadLock





C:\Users\stateis0>jstack 11820
2017-12-29 18:52:38
Full thread dump Java HotSpot(TM) Client VM (25.131-b11 mixed mode):

"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.


C:\Users\stateis0>
Copy the code

Thread-1 waiting to lock <0x07415a50> locked <0x0742bd18> Thread-0 waiting to lock <0x0742bd18> locked <0x07415a50>

We first use the JPS command to find the Java process number, and then use the jstack process number to print the process stack information. In the last section, JStack tells us that it found a deadlock, which contains more detailed information: Thread-1 the Thread holds the lock 0x07415a50 of type String and waits for the lock 0x07415a50. But this lock is held by Thread0, which is the opposite of Thread1. Thread-0 the Thread holds the lock at 0x07415a50 and waits for the lock at 0x07415a50. It’s in our notes.

So what happens when a deadlock occurs? The easiest way to do this is to reboot, after which you modify the code in the stack information printed in jStack. Republish. There are also advanced strategies, such as rolling back processes to their pre-deadlock state and then ordering them into synchronized blocks.

3. What are the causes of deadlocks

In general, the following conditions must be met for a deadlock problem to occur:

  1. Exclusive condition: a resource can only be used by one thread at a time.

  2. Request and hold conditions: when a process is blocked by requesting resources, it holds on to acquired resources.

  3. Non-dispossession condition: a process cannot forcibly take away a resource it has acquired until it is used up.

  4. Circular waiting condition: a circular waiting resource relationship is formed between several processes.

Deadlocks are caused by four prerequisites, so in general, if one of these prerequisites is broken, deadlock situations should not occur.

If we want to break the mutex condition, we need to allow the process to access some resources at the same time. This method is not easy to implement conditions due to the actual situation.

Breaking the non-preemption condition, which requires that processes be allowed to forcibly take certain resources from the possessor, or simply understand that processes that possess resources cannot apply for other resources and must release the resources in hand before they can apply, which is also difficult to find a suitable scenario;

A process must obtain all resources before running. Otherwise, the process cannot enter the ready state. This approach may seem useful, but the downside is that it can reduce resource utilization and process concurrency;

To avoid resource application loops, you need to number resources by number in advance. This method can effectively improve the utilization of resources and system throughput, but increase the system overhead, increase the process to occupy the time of resources.

4 summarizes

There are many pits in concurrent programming, especially deadlocks, which cause problems that can only be solved by restarting. If data is saved in memory but not persisted, then restarting will be a big problem. So we must be careful when we use locks. Avoid deadlocks. If deadlocks occur, you can use the jstack command to see if the thread has deadlocks. Used to troubleshoot problems.

In short, there are a lot of concurrent pits, the landlord will be more analysis later.

Good luck !!!!