Welcome to Concurrency King Lessons, the 11th article in a series.

In this article, I will introduce you to the classic problem of multi-threading – deadlocks, as well as the causes of deadlocks, how to deal with them, and how to prevent them.

First, the generation of deadlock

If you look at the diagram below, thread 1 holds A, but it needs B; Thread 2 holds B, but it needs A.

The problem, you see, is that both A and B are waiting for resources that the other person already has, and neither is releasing them. This brings things to A deadlock, A deadlock.

In concurrent programming, a deadlock represents a state. In this state, each party is waiting for the other party to release the resources they hold, but they lack the necessary communication mechanism between each other, resulting in the existence of loop dependence and waiting forever.

Deadlocks are not only found in Java programs, but also in other middleware and distributed architectures such as databases. Deadlock detection and recovery are taken into account in the design of the data. When a death lock is issued in the database, a victim is selected and the corresponding transaction is abandoned, while the locked resource is released. After its competitor finishes executing, the application can rerun the transaction because its competitor has previously completed the transaction.

Deadlocks, however, are not handled as elegantly in a JVM as they are in a database. When a set of threads deadlock, the “game” is over, and these threads are no longer available, which can directly cause the application to crash, degrade performance, or stop some functionality.

So, as with any concurrency problem, deadlocks are dangerous, their effects are immediately apparent, and under high loads, they can be a disaster.

Second, the necessary conditions for the generation of deadlock

From the diagram in the first section, we can see some of the conditions necessary for a deadlock to occur:

  1. Mutual exclusion: A resource can only be used by one thread at a time. For example, in the figure above, A and B can only be used by either thread 1 or thread 2 at the same time.
  2. Request and hold condition: A thread holds on to a resource it already holds while the request for another resource is blocked. For example, thread 1 in the figure above does not release A when it requests B;
  3. Do not deprive a thread of a resource that it has acquired before it voluntarily releases it. For example, threads 1 and 2 in the figure above cannot be forcibly stripped of resources they have acquired unless they themselves release them.
  4. Loop wait condition: A loop wait is formed between threads. Threads 1 and 2 in the figure above form a circular wait.

3. Simulate and experience deadlock

After understanding what a deadlock is and the conditions under which it occurs, let’s simulate the occurrence of a deadlock through a piece of code based on the deadlock situation in the figure above.

As shown in the figure above, the Nezha thread is defined to hold A and request B at run time:

Private static class implements Runnable {public void run() {System.out.println(); private static class implements Runnable {public void run() {System.out.println(); ); try { Thread.sleep(10); } catch (interruptedException) {} System.out.println(" Nezha: wait for B...") {} InterruptedException ignored) {} System.out.println(" Nezha: wait for B...") ); Synchronized (lockB) {System.out.println(" Nezha: Has both A and B..." ); }}}}

Define the Lanling King thread to hold B at runtime and request A:

private static class LanLingWang implements Runnable { public void run() { synchronized(lockB) { Println (" Lanling King: Hold B!") ); try { Thread.sleep(10); } catch (interruptedException) {} System.out.println(" A... ); Synchronized (Locka) {System.out.println(" Synchronized (Locka) {System.out.println(" Synchronized (Locka) {System.out.println(); ); }}}}

Start two threads:

public class DeadLockDemo { public static final Object lockA = new Object(); public static final Object lockB = new Object(); public static void main(String args[]) { Thread thread1 = new Thread(new NeZha()); Thread thread2 = new Thread(new LanLingWang()); thread1.start(); thread2.start(); }}

The output of the two threads is as follows:

Nezha: Hold A! Lanling King: Hold B! Nezha: Waiting for B... King Lanling: Waiting for A...

As can be seen from the results, Nezha and Lanling King hold A and B respectively, but they request each other’s resources, which eventually leads to A deadlock and the two threads enter an infinite wait.

IV. Deadlock Handling

1. Ignore deadlocks

Ignoring deadlocks is an ostrich policy that assumes they will never happen. This strategy is suitable for scenarios where the probability of a deadlock is low and the impact is tolerable, or it can be used if the deadlock proves to never occur.

2. The test

Deadlocks are allowed under this strategy. If the system detects a deadlock, it also fixes it, such as tracking thread state and resource allocation. In the event of a deadlock, there are several ways to correct it:

  • Thread termination: select one or more threads to terminate, release resources and break the deadlock state;
  • Resource preemption: Redistributes resources preempted by each thread until the deadlock is broken.

3. To prevent

Prevention is the key to dealing with deadlocks. The second section of this article has listed some of the conditions necessary for a deadlock to occur, so if you want to prevent a deadlock, you only need to break one of these conditions. We will cover the exact way to pre-issue a deadlock in Java in a later article.

summary

So that’s all about deadlocks. In this article, we introduced what a deadlock is, the conditions necessary for it to occur, and the strategies for dealing with it. Deadlocks in development should be treated in awe, but not in awe. Carefully analyzing the possibility of deadlocks and designing appropriate strategies can effectively prevent them.

The text ends here, congratulations you went up a star ✨ again

Teacher’s trial

  • Run the sample code in this article to try to find a way to break its deadlock.

Extended reading and references

  • A deadlock
  • Java Concurrency in Practice
  • Deadlock prevention algorithms
  • “Concurrent king lesson” outline and update progress overview

About the author

Pay attention to the public number [mediocre technology joke], access to timely article updates. Record the technical stories of ordinary people, share quality (as much as possible) technical articles, and occasionally talk about life and ideals. No anxiety peddling, no headline peddling.

If this article is helpful to you, welcome thumb up, follow, monitor, we together from bronze to king.