A Dead Lock is a two- or more units of operation (process, thread, or coroutine) that are waiting for the other to stop executing to obtain system resources, but no party exits early, which is called a deadlock.

The formation of deadlocks is divided into two aspects, one is a deadlock formed using the built-in lock synchronized, and the other is a deadlock implemented using the explicit lock Lock, let’s look at them separately.

The results of the above procedure are as follows:

As can be seen from the above results, both thread 1 and thread 2 are waiting for each other to release the lock, which creates a deadlock problem.

The results of the above procedure are as follows:

Using the above examples, we can conclude that the following four conditions are required to produce a deadlock:

Only if the above 4 conditions are met at the same time will the deadlock problem be caused.

If a program has a deadlock problem, you can analyze and troubleshoot it in any of the following four scenarios.

Before we use jstack, we must first get the process ID of the running program through jps, using the following methods:

“jps -l” can query all Java programs locally, jps (Java Virtual Machine Process Status Tool) is a command provided by Java to display all current Java process pids, suitable for simple viewing of the current Java process on the linux/unix/windows platform, “-l” is used to output the process pid and run the full path name (package name and class name).

Now that we have the process ID (PID), we can use “jstack -l PID” to find the deadlock problem, as shown in the following figure:

jstack is used to take a snapshot of the thread at the current moment of the Java virtual machine, with “-l” for a long list (long) that prints additional information about the lock.

PS: You can use jstack -help to see more command instructions.

To use jconsole, you need to open the bin directory of the JDK, find jconsole and double-click to open it, as shown in the following figure:

Then select the program you want to debug, as shown in the following figure:

After that, click Connect to enter and select “Insecure Connection” to enter the monitoring homepage, as shown in the following figure:

After that, switch to the “Threads” module and click the “Detect Deadlocks” button, as shown in the following figure:

After a few moments, information about the deadlock will be detected, as shown in the following figure:

jvisualvm is also in the bin directory of the JDK, which is also opened by double-clicking:

After a few seconds, all local Java programs appear in jvisualvm, as shown in the following figure:

Double-click to select the program you want to debug:

Click the mouse to enter the Threads module, as shown in the following figure:

As can be seen from the above figure, when we switch to the thread column, the deadlock information will be displayed directly, and then click “Thread Dump” to generate the details of the deadlock, as shown in the following figure:

jmc, short for Oracle Java Mission Control, is a suite of tools for managing, monitoring, profiling, and troubleshooting Java programs. It is also in the bin directory of the JDK, and it is also launched by double-clicking, as shown in the following figure:

The jmc homepage is as follows:

After that, select the program you want to troubleshoot, and right-click “Start JMX Console” to view the details of this program, as shown in the following figure:

Then click “Thread” and check “Deadlock Detection” to find the details of deadlocks and deadlocks, as shown in the following figure:

Next, let’s analyze the 4 conditions that produce deadlocks, which ones can be broken? What cannot be destroyed?

Through the above analysis, we can conclude that we can only solve the deadlock problem by destroying the request and hold condition or the loop waiting condition, and then go online, we will first solve the deadlock problem by breaking the “loop waiting condition”.

The so-called sequential lock refers to the solution to the deadlock problem by acquiring the lock sequentially, thereby avoiding the creation of loop wait conditions.

When we are not using sequential locks, the execution of the program may look like this:

Thread 1 acquires lock A first, then acquires lock B, thread 2 executes at the same time as thread 1, and thread 2 acquires lock B first, and then acquires lock A, so that both sides first occupy their respective resources (lock A and lock B) and then try to acquire each other’s locks, resulting in loop waiting problems and finally deadlocks.

At this point, we only need to unify the order in which thread 1 and thread 2 acquire locks, that is, after thread 1 and thread 2 execute at the same time, they both acquire lock A first, and then acquire lock B, the execution process is as shown in the following figure: because only one thread can successfully acquire lock A, the thread that does not acquire lock A will wait to acquire lock A first, and the thread that gets lock A continues to acquire lock B, because there is no thread scramble and owns lock B, then the thread that gets lock A will successfully own lock B. The corresponding code is then executed and the lock resource is all released, and then another thread waiting to acquire lock A can successfully acquire the lock resource and execute the subsequent code, so that there is no deadlock problem.

The implementation code for the sequential lock is as follows:

The results of the above procedure are as follows:

As can be seen from the above execution results, the program does not have a deadlock problem.

Polling locks is to avoid deadlocks by breaking the “request and hold condition”, and its implementation idea is simply to try to acquire locks by polling, and if one lock acquisition fails, release all locks owned by the current thread, wait for the next round and then try to acquire locks.

The implementation of polling locks requires the use of the tryLock method of ReentrantLock as follows:

The results of the above procedure are as follows:

As can be seen from the above results, the above code does not have a deadlock problem.

Using polling locks can solve the problem of deadlocks, but it is not perfect, such as the following problems.

The above simple version of the polling lock, if you encounter a thread has been overcharging or occupying the lock resource for a long time, it will cause the polling lock to enter an endless loop state, it will try to acquire the lock resource all the time, which will cause new problems and bring unnecessary performance overhead, as follows.

The execution result of the above code is as follows:

As you can see from the above results, Thread 1 polls the lock into an dead-loop state.

In view of the above dead loop situation, we can improve the following two ideas:

Any one of the above strategies can solve the problem of dead loops, for the sake of implementation costs, we can use the maximum number of polling methods to improve the polling lock, the specific implementation code is as follows:

The execution result of the above code is as follows:

As can be seen from the above results, when we improve, the polling lock will not have the problem of an endless loop, and it will try a certain number of times and then terminate the execution.

The polling wait time for our above polling lock is a fixed time, as shown in the following code:

This causes threads to starve in special cases, that is, the problem that polling for locks has not been able to acquire, such as the following example.

The execution result of the above code is as follows:

As can be seen from the above results, thread 1 (polling lock) has not successfully acquired the lock, and the reason for this result is that thread 1 waits for a fixed 1s per poll, and thread 2 is the same frequency, acquiring a lock every 1s, which will cause thread 2 to successfully acquire the lock first, while thread 1 will always be in a “starvation” situation, the execution process is as shown in the following figure:

Next, we can improve the fixed wait time of polling locks to a fixed time + random time, so as to avoid the problem of “starving” of polling locks because the frequency of acquiring locks is consistent, and the specific implementation code is as follows:

The execution result of the above code is as follows:

As can be seen from the above results, thread 1 (polling locks) does not starve to death after adding a random wait time.

This article introduces the concept of deadlocks, as well as the 4 conditions that produce deadlocks, can be detected by any of the 4 tools provided in this article, from the perspective of ease of use and performance, it is recommended to use jconsole or jvisualvm, and finally we introduce two solutions to the deadlock problem: sequential locks and polling locks.

—END—

In addition, I recently intend to turn the new technology exchange group into an active high-quality technology group. From time to time, there are book delivery, red envelope distribution activities, technical problems encountered in the work, you can consult everyone inside, and there are opportunities to push within the work. Interested friends, welcome to add groups.

Scan the QR code below, remarks: add the group, you can enter the group.