What is a deadlock? Do you understand the bytecode instructions of deadlock?

Use humorous and simple language to describe deadlock

Bansheng: I have already won the first place in the computer-based test, and I am just missing the first place in the written test.

Primary One: I have already won the first place in the written test, and I am just missing the first place in the computer-based test.

Interviewer: I'm very optimistic about you two. Keep working. Only if you get the first place in the two at the same time can you get an offer and enter my big factory XX.

Ban Sheng: Junior One, you ranked first in the written test, how about letting me do it?

Primary One: In my dreams, I also want you to get first place in the computer-based test!

Ban Sheng: It’s at your level, zz

Xiao Yi: WTF, let’s have a fight

Bansheng: Come on, who is afraid of who?

So: a deadlock occurs, the brave one wins in a narrow encounter, and the computer crashes.

What is a deadlock? What are the conditions for deadlock to occur?

1. Deadlock refers to a situation in which two or more threads wait for each other to release the resources they hold, resulting in the process being unable to continue execution. Specifically, when a deadlock occurs, the thread will enter a permanent waiting state, unable to continue execution and eventually cause the program to become unresponsive or crash.

2. The conditions that produce deadlock are usually called the four necessary conditions for deadlock, including:

  1. Mutual Exclusion: At least one resource is exclusively occupied by multiple threads, which means that a resource can only be occupied by one thread at the same time.
  2. Request and hold conditions (Hold and Wait): The thread already holds at least one resource and is waiting to obtain resources occupied by other threads.
  3. No Preemption: The acquired resource cannot be preempted by other threads and can only be explicitly released by the thread that owns it.
  4. Circular Wait Condition (Circular Wait): Multiple threads form a circular waiting chain, that is, each thread is waiting for the resource held by the next thread.

Deadlock may occur only when the above four conditions are met at the same time.

Below is the previous code (the code in question will test your eyesight and basic skills)

 public static void main(String[] args) {
        final Object resource1 = new Object();
        final Object resource2 = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("Thread 1:锁住offer1");
            }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("Thread 1:锁住offer2");
                }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("Thread 2:锁住offer2");
            }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource1) {
                    System.out.println("Thread 2:锁住offer1");
                }
        });

        thread1.start();
        thread2.start();
    }

Why can't these two threads lock this offer?

7c7be128d11d4408a42624aab68a8cfe.png

The good news should not go to outsiders. Since you can't lock in this offer, let me do it~

public static void main(String[] args) {
        final Object resource1 = new Object();
        final Object resource2 = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("半生:锁住了机考第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("半生:笔试第一名");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("小一:锁住了笔试第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource1) {
                    System.out.println("小一:锁住机考第一名");
                }
            }
        });

        thread1.start();
        thread2.start();
    }

6187c605ae9847b1b2d39a5eccaab3af.png

It's you two again, what are you doing? It's a deadlock. There are more than one offers. Can't you get along well and have fun and get offers at the same time?

Let me take a look at how the bytecode instructions are executed.

1. First use javac -encoding UTF-8 X.java to generate class files

2. javap -verbose X.class decompilation

3. Judging from the decompiled instructions, it should be that the operating system or the JVM virtual machine has detected a deadlock and forced an interruption. When using synchronized as a lock, we know that there is a pair of instructions monitorenter monitorexit. But I didn’t see it here

4. Download an idea plug-in jclasslib to check whether the bytecode instructions are consistent.

5. You can see that there is a pair of lock instructions here. Here is a brief explanation of the meaning of the upper-write bytecode instructions.

0 aload_0:加载索引为0的引用到操作数栈,通常用于加载实例方法的隐式参数,即this。

1 dup:复制栈顶的元素,并将复制后的值重新压入栈顶。

2 astore_2:将栈顶的引用类型数值存储到局部变量表的索引为2的位置。

3 monitorenter:进入同步块前获取锁。

4 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>:获取静态字段System.out的值,即标准输出流PrintStream对象。

7 ldc #12 <小一:锁住了笔试第一名>:将常量池中索引为12的String类型常量加载到操作数栈。

9 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。

12 ldc2_w #13 <1000>:将常量池中索引为13的long类型常量加载到操作数栈。

15 invokestatic #15 <java/lang/Thread.sleep : (J)V>:执行Thread类的静态方法sleep,其中参数为栈顶的long类型常量。

18 goto 33 (+15):无条件跳转到字节码指令33,即跳过下方的指令。

21 astore_3:将栈顶的引用类型数值存储到局部变量表的索引为3的位置。

22 getstatic #3 <java/lang/System.out : Ljava/io/PrintStream;>:获取静态字段System.out的值。

25 ldc #17 <小一:被中断,释放笔试资源>:将常量池中索引为17的String类型常量加载到操作数栈。

27 invokevirtual #5 <java/io/PrintStream.println : (Ljava/lang/String;)V>:执行PrintStream对象的println方法,其中参数为栈顶的String类型常量。

30 aload_2:加载局部变量表中索引为2的引用类型数值到操作数栈。

31 monitorexit:退出同步块,释放锁。

32 return:返回void类型的值,并结束当前方法。

33 aload_1:加载局部变量表中索引为1的引用类型数值到操作数栈。

34 dup:复制栈顶的元素,并将复制后的值重新压入栈顶。

35 astore_3:将栈顶的引用类型数值存储到局部变量表的索引为3的位置。

Here's the command

No. 9 invokevirtual #5 <java/io/PrintStream.println: (Ljava/lang/String;)V>: Execute the println method of the PrintStream object, where the parameter is the String type constant on the top of the stack. The print log in the catch is executed, indicating that the execution was interrupted, and then goto 33 jumps to the instruction on line 33.

6. In addition, I wrote another synchronization method, let’s take a look

It can be seen from here that it is locked. There is an additional monitorexit on line 20. This is to prevent abnormal forced release of the lock, which is the guarantee that synchronized can automatically release the lock.

So: Here comes the way to solve the deadlock~

Take a step back and think about the sky, hello, me, hello everyone

public static void main(String[] args) {
        final Object resource1 = new Object();
        final Object resource2 = new Object();

        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("半生:获得了机考第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("半生:获得了笔试第一名");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("小一:获得了笔试第一名");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                synchronized (resource2) {
                    System.out.println("小一:获得了机考第一名");
                }
            }
        });

        thread1.start();
        thread2.start();
    }

85cc84572eba4e8b85376390faadb889.png

As long as I slightly changed the order of obtaining resources, Bansheng and Primary 1 both passed the computer-based test, ranked first in the written test, and both received offers from XX manufacturers.

There are many ways to break deadlock, as long as one of the four necessary conditions for deadlock is removed.

The following common methods are commonly used to solve deadlock problems :

  1. Avoid circular waiting: By stipulating the order in which resources are locked, threads are prevented from waiting for each other's resources. You can agree on the order of resource acquisition through sorting or numbering to avoid circular waiting.

  2. Destroy request and hold conditions: Allow a thread to obtain all required resources at once when requesting resources, or release already occupied resources when obtaining a certain resource. This avoids the situation where one thread holds one resource and waits for another resource to be released.

  3. Use resource deprivation: When a thread requests a resource, if the resource is already occupied by other threads, it can temporarily deprive other threads of the lock on the resource to meet the needs of the current thread. The deprived thread can wait for a period of time before reapplying for resources.

  4. Use a timeout mechanism: Set a timeout when acquiring a lock resource. If the resource cannot be obtained within the specified time, give up the acquisition, release the occupied resource, and then try again.

  5. Deadlock detection and recovery: By detecting deadlock situations in the system, the deadlocked threads can be recovered or terminated. Common deadlock detection algorithms include resource allocation graph algorithm and banker's algorithm.

It should be noted that different solutions are suitable for different scenarios and problems, and choosing the appropriate method needs to be evaluated based on the specific situation. Also, it is better practice to prevent deadlock problems. During design and implementation, try to avoid conditions that may lead to deadlock, and fundamentally eliminate the occurrence of deadlock problems.

Guess you like

Origin blog.csdn.net/weixin_42450130/article/details/132672163