Java Concurrent Programming: the formation condition and treatment of deadlock in concurrency

Time:2021-3-4

Deadlock is an infinite state of waiting for each other. Two or more threads or processes form a ring of waiting for each other. Taking two threads as an example, thread one holds a lock while waiting for b lock, while thread two holds b lock while waiting for a lock, which leads to two threads waiting for each other and unable to execute. A classic deadlock situation in real life is that four cars pass through the intersection without traffic lights. If four cars arrive at the center at the same time, they will form a deadlock state. Each car has the right to use its own lane, but it is also waiting for another car to give up the right to use another lane

Java Concurrent Programming: the formation condition and treatment of deadlock in concurrency

Examples of deadlocks

In this example, there are two locks, lock1 and lock2. As soon as the thread starts, it first attempts to acquire the lock1 lock, and then continues to attempt to acquire the lock2 lock after successfully acquiring the lock1 lock. Thread 2 first attempts to acquire the lock2 lock, and then continues to attempt to acquire the lock1 lock after successfully acquiring the lock2 lock. When we start the program one time, the possible output is as follows, and it will enter the deadlock state, but not every time. Each thread sleeps for 100 milliseconds to increase the possibility of deadlock. Finally, the two threads are waiting for each other wirelessly. The thread that obtains lock1 is waiting for lock2, while the lock that obtains lock2 is waiting for lock1.

Java Concurrent Programming: the formation condition and treatment of deadlock in concurrency

Java Concurrent Programming: the formation condition and treatment of deadlock in concurrency

Deadlock handling

Because deadlock detection involves many complex scenarios, and it is only generated at runtime, programming language compilers generally do not provide deadlock detection function, including Java. This is actually called ostrich algorithm. If we don’t have a good way to deal with something, we can just bury our head in the sand like ostrich and pretend that we can’t see anything. Deadlock scenario processing is handed over to the actual programming developers. Developers need to avoid deadlock or formulate some measures to deal with the deadlock scenario. Common deadlock handling methods can be roughly divided into two categories: one is preventive measures in advance, including lock sequencing, resource merging, avoiding lock nesting and so on. The other is the post-processing measures, including lock timeout mechanism, preemptive resource mechanism, thread revocation and so on. Now let’s take a detailed look at each measure.

The sequence of locks changes

We can break this condition to avoid deadlock. Specifically, the acquisition of locks is sequenced. All threads and processes acquire locks in the specified order. For example, in the figure below, P1, P2 and P3 all try to hold R1 lock first, then R2 lock, and finally R3 lock. Of course, it can also be seen that to obtain R3 lock, R2 lock must be obtained first, while to obtain R2 lock, R1 lock must be obtained first. This will break the ring condition and avoid deadlock.

Java Concurrent Programming: the formation condition and treatment of deadlock in concurrency

Resource consolidation

The method of resource merging is to treat multiple resources as one resource. In this way, the acquisition of multiple resources can be changed into the acquisition of only one resource, so as to avoid the occurrence of deadlock. As shown in the figure below, resource R1, resource R2 and resource R3 are combined into one resource R, and then three threads get it.

Java Concurrent Programming: the formation condition and treatment of deadlock in concurrency

Avoid lock nesting

The nesting behavior of lock acquisition operation may lead to deadlock, so we can remove lock nesting to avoid deadlock. Each thread releases one resource after using it, and then obtains another resource, and releases it after using it. This is to remove lock nesting. As shown in the figure below, thread P1 holds R1 lock and releases it, then holds R2 lock and releases it, and finally holds R3 lock and releases it. Other threads operate similarly.

Java Concurrent Programming: the formation condition and treatment of deadlock in concurrency

Lock timeout mechanism

The first measure of post-processing is the lock timeout mechanism. The core is that the waiting for a lock is not permanent, but has a timeout. If a thread’s waiting for a lock exceeds the specified time, it will do the timeout processing and end the thread directly. For example, in the figure below, three threads have entered the deadlock state. If the time that thread P1 waits for R2 lock exceeds the timeout time, P1 will end and release the possession of R1 lock. At this time, the thread P3 can obtain the R1 lock, so it can release the wait, and then the deadlock state is released.

Java Concurrent Programming: the formation condition and treatment of deadlock in concurrency

summary

This paper mainly introduces the deadlock related content, in addition to introducing the concept of deadlock, we also provide examples of deadlock, as well as the conditions for the formation of deadlock, and the way to deal with deadlock. Deadlock processing mainly includes pre preventive measures such as lock sequencing, resource merging, avoiding lock nesting, and processing measures such as timeout mechanism, resource preemption mechanism, thread revocation mechanism, etc

Java Concurrent Programming: the formation condition and treatment of deadlock in concurrency