67-How to write an example of inevitable deadlock?

What is a deadlock? What's the harm?
What is a deadlock

Occurs in concurrency

First of all, you need to know that deadlock must occur in concurrent scenarios. In order to ensure thread safety, we sometimes use various tools that can ensure concurrency safety for the program, especially locks, but if handled improperly during use, it may lead to deadlocks.

Not give way to each other

A deadlock is a state. When two (or more) threads (or processes) hold the resources needed by each other, but they do not actively release the resources held in their hands, everyone cannot obtain themselves. For the desired resources, all related threads (or processes) cannot continue to execute, and cannot move forward until this state is changed. We call this state a deadlock state and think that they are dead. lock. In layman's terms, a deadlock is a state in which two or more threads (or processes) are blocked indefinitely, waiting for each other's resources.

Examples in life

Below we use the method of illustration to show a deadlock situation in life, as shown in the following figure:
Insert picture description here
You can see this cartoon showing a scene where two gentlemen bow to each other separately, in order to show politeness, after they bend down No one wants to get up first, and hope that I will get up again after the other person gets up. But in this way, no one can get up first, and the action of getting up has been unable to continue. The two formed a state of waiting for each other, so this is a typical deadlock!

Example of two threads

Let’s take a look at the deadlock situation of two threads in the form of animation, as shown in the following figure:
Insert picture description here
At this time, we have two threads, thread A and thread B. Assuming thread A now holds lock A, thread B holds lock B, and thread A tries to acquire lock B. Of course, it cannot acquire lock B because thread B has not released lock B. Then thread B tries to acquire lock A again. Similarly, thread B cannot acquire lock A because lock A is already held by thread A. In this way, thread A and thread B are deadlocked, because they both hold the resources that each other wants, but do not release the resources in their hands, forming a mutual wait, and they will wait forever.

Deadlock caused by multiple threads

Deadlocks exist not only in two-thread scenarios, but also in multiple threads. If the dependency between multiple threads is circular and there is a circular dependency, then deadlock may also occur, as shown in the following figure:
Insert picture description here
we see in this example, first thread 1 holds lock A, and then Thread 2 holds lock B, then thread 3 holds lock C, and now each thread holds a lock. Next, thread 1 wants to hold lock B, but it can’t get it, because lock B is in the hands of thread 2; then thread 2 tries to get lock C again, and it can’t get it either, because now the lock C is in the hands of thread 3; then thread 3 tries to acquire lock A, of course it cannot acquire lock A, because lock A is now in the hands of thread 1, so that thread 1, thread 2 and thread 3 are mutually exclusive A loop is formed, and this is what happens when a deadlock occurs in multiple threads. So not only two threads, but multiple threads may also deadlock.

The impact of deadlock

The impact of deadlock is different in different systems, and part of the impact depends on the current system or environment's ability to handle deadlock.

In the database

For example, in the design of database system software, the monitoring of deadlocks and the recovery from deadlocks are considered. When executing a transaction, you may need to acquire multiple locks and hold these locks until the transaction is completed. The lock held in a transaction may also be required in other transactions, so a deadlock may occur between two transactions. Once a deadlock occurs, if there is no external interference, then the two transactions will be forever Wait for it. But the database system will not let this happen. When the database detects that this group of transactions is deadlocked, depending on the strategy, it may choose to abandon a certain transaction , and the abandoned transaction will release its holdings. The lock, so that other transactions continue to proceed smoothly. At this point, the program can re-execute the forcibly terminated transaction, and this transaction can now be executed smoothly, because all the transactions competing with it for resources have been executed just now, and the resources have been released.

JVM

In the JVM, the deadlock processing capability is not as powerful as the database. If a deadlock occurs in the JVM, the JVM will not automatically handle it , so once the deadlock occurs, it will fall into endless waiting.

The odds are not high but the harm is great

The problem of deadlock is probabilistic like other concurrency security problems, that is to say, even if there is a possibility of deadlock, it will not happen 100%. If the holding time of each lock is very short, then the probability of conflicts is very low, so the probability of deadlocks is also very low. However, in the online system, there may be tens of millions of "acquisition lock" and "release lock" operations every day. In the face of a huge number of times, the probability of problems in the entire system will be magnified . Risky, it may lead to deadlock.

It is precisely because of the feature that deadlocks "may not happen", it has become a difficult problem to find out deadlocks in advance. Although stress testing can detect some situations where deadlock may occur, it is not enough to fully simulate real, long-running scenarios, so there is no way to find out all potentially deadlock code .

Once a deadlock occurs, depending on the responsibilities of the thread where the deadlock occurs, various undesirable consequences such as subsystem crashes, performance degradation, and even the entire system crash may occur. Moreover, deadlocks often occur under high concurrency and high load conditions, because they may directly affect many users and cause a series of problems. The above is the low probability of deadlock but the characteristics of great harm.

Examples of deadlock

Below we give an example of a deadlock that will inevitably occur, the code is as follows:

/**
 * 描述:     必定死锁的情况
 */
public class MustDeadLock implements Runnable {
    
    
    public int flag;
    static Object o1 = new Object();
    static Object o2 = new Object();
    public void run() {
    
    
        System.out.println("线程"+Thread.currentThread().getName() + "的flag为" + flag);
        if (flag == 1) {
    
    
            synchronized (o1) {
    
    
                try {
    
    
                    Thread.sleep(500);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
                synchronized (o2) {
    
    
                    System.out.println("线程1获得了两把锁");
                }
            }
        }
        if (flag == 2) {
    
    
            synchronized (o2) {
    
    
                try {
    
    
                    Thread.sleep(500);
                } catch (Exception e) {
    
    
                    e.printStackTrace();
                }
                synchronized (o1) {
    
    
                    System.out.println("线程2获得了两把锁");
                }
            }
        }
    }
    public static void main(String[] argv) {
    
    
        MustDeadLock r1 = new MustDeadLock();
        MustDeadLock r2 = new MustDeadLock();
        r1.flag = 1;
        r2.flag = 2;
        Thread t1 = new Thread(r1, "t1");
        Thread t2 = new Thread(r2, "t2");
        t1.start();
        t2.start();
    }
}

As you can see, there is an int type flag in this code, which is a flag bit, and then we created o1 and o2 as synchronized lock objects.

Let's take a look at the run method. In the run method, it will first print out the name of the current thread, and then print out the value of the flag of the current thread.

If flag is equal to 1, it will first acquire the lock of o1, then sleep for 500 milliseconds, then try to acquire the lock of o2 and print out "Thread 1 has acquired two locks".

If flag is equal to 2, then the situation is just the opposite. The thread will first acquire the lock of o2, then sleep for 500 milliseconds, then acquire the lock of o1, and print out "Thread 2 has acquired two locks."

Finally, let’s take a look at the main method. Two instances of this class are created in the main method, that is, two Runnable objects, and their flags are changed to 1 and 2: the flag of r1 is set to 1, and the flag of r2 is set. Set to 2. Then create two new threads to execute these two Runnable objects respectively. The names of the two threads that execute r1 and r2 are called t1 and t2, respectively, and finally we start the two threads.

An execution result of the program:

线程t1的flag为1
线程t2的flag为2

The important point here is that the program execution continues to execute at this time without stopping, and it will never print the statement "Thread 1 has acquired two locks" or "Thread 2 has acquired two locks". At that time, a deadlock occurred here.

Analyze the process of deadlock

Below we analyze the above process of deadlock:

  • When the first thread runs, it will find that its flag is 1, so it will try to obtain the lock o1 first, and then sleep for 500 milliseconds.
    Insert picture description here

  • While thread 1 starts and sleeps, thread 2 will also start. Since the flag of thread 2 is 2, it will enter the code block corresponding to the following if (flag == 2), and then thread 2 will first acquire the lock of o2. That is to say, during the sleep period after thread 1 starts and acquires the lock of o1, thread 2 acquires the lock of o2, and then thread 2 also starts to sleep for 500 milliseconds.
    Insert picture description here

  • When the 500 millisecond sleep time of thread 1 ends, it will try to acquire the lock of o2. At this time, the lock of o2 is being held by thread 2, so thread 1 cannot acquire o2.

Insert picture description here

  • Thread 2 will also wake up immediately, and it will try to acquire the lock of o1. At this time, o1 has been held by thread 1.

Insert picture description here
So the current state is that thread 1 is stuck in the position of acquiring the lock of o2, and thread 2 is stuck in the position of acquiring the lock of o1 , so that thread 1 and thread 2 form a mutual wait and need to be held by each other Resources can continue to execute, thus forming a deadlock. In this example, if thread 2 starts before thread 1, the situation is similar, and a deadlock will eventually occur. This is an "inevitable deadlock example".

Insert picture description here

to sum up

Having said that, what is a deadlock, and then introduced examples of deadlock in life, in two threads, and in multiple threads. Then we analyzed the impact of deadlock. If a deadlock occurs in the JVM, it may cause part or all of the program to be unable to continue to execute downwards. Therefore, the harm and impact of the deadlock in the JVM is relatively large. We need to avoid as much as possible. Finally, an example code that is bound to deadlock is given, and this code is analyzed in detail.

Guess you like

Origin blog.csdn.net/Rinvay_Cui/article/details/111059247