Multithreading -- deadlock

Posted by Wade on Sat, 19 Feb 2022 23:28:32 +0100

What is deadlock?

Multiple threads occupy some shared resources and wait for the resources occupied by other threads to run. As a result, two or more threads are waiting for each other to release resources and stop execution When a synchronization block has "locks of more than two objects" at the same time, the phenomenon of "deadlock" may occur

example:

package com.company;
//mirror
//lipstick

class LipsTick {
}

class Mirror {
}

 class makeUp extends Thread {
     static LipsTick lipsTick = new LipsTick();//static ensures that only one shared resource is shared by all instances of this class
    static Mirror mirror = new Mirror(); //The memory address remains unchanged. Even if the object of this class is re established, the address of the object remains unchanged
    int id;
    String name;

    makeUp(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public void makeUp() throws InterruptedException {
        if (this.id == 0) {
            synchronized (mirror) {         //Get the mirror and monopolize the mirror resources
                System.out.println(this.name + "Got the mirror");
                Thread.sleep(1000);      //Blocking this thread and letting another thread enter will not release the lock
                synchronized (lipsTick) {  //I also want lipstick
                    System.out.println(this.name + "Got lipstick");
                }
            }
        } else {
            synchronized (lipsTick) { //Enter the second thread, obtain lipstick and monopolize the lipstick resource
                System.out.println(this.name+ "Got lipstick");
                Thread.sleep(1000);   //Block this thread and let another thread enter
                synchronized (mirror) {
                    System.out.println(this.name + "Got the mirror");
                }
            }
        }
    }
    public void run(){
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        makeUp m1 = new makeUp(0, "Cinderella");
        makeUp m2 = new makeUp(1, "Snow White");
        m1.start();
        m2.start();
    }
}

The results are as follows:

analysis:
The code contains two threads, namely "Cinderella" and "Snow White". Two shared resources lipsTick and mirror are defined in the test class, and static (object memory address unchanged) ensures that there is only one copy of the two resources

synchronized (mirror) allows "Cinderella" to monopolize the resource of mirror, then use sleep to block the thread, and then change the thread "Snow White". Similarly, "Snow White" uses synchronized (lipsTick) to monopolize the resource lipsTick, then uses sleep to block the current thread, and then cpu scheduling enters "Cinderella" again "Thread, the thread continues to execute the nested synchronized (lipsTick) statement," Cinderella "wants to obtain the lipsTick resource, but the resource has been locked and monopolized by the resource of the thread where" Snow White "is located, which forms the" Cinderella "exclusive mirror resource," Snow White " Monopolize lipsTick resources, and both parties want to obtain each other's resources, so the program is always in a waiting state, that is, the so-called deadlock

Deadlock diagram:

How to prevent deadlock?

  • Four conditions for deadlock generation
    1. Mutex condition: a resource can only be used by one process at a time
    2. Request and hold condition: when a process is blocked by requesting resources, it holds on to the obtained resources
    3. Conditions of non deprivation: the resources obtained by the process cannot be forcibly deprived before they are used up
    4. Circular waiting condition: a circular waiting resource relationship is formed between several processes

The four necessary conditions for deadlock are listed above. We can avoid deadlock as long as we try to break one or more of them

Modified code:

package com.company;
//mirror
//lipstick

class LipsTick {
}

class Mirror {
}

class makeUp extends Thread {
    static LipsTick lipsTick = new LipsTick();//static ensures that only one shared resource is shared by all instances of this class
    static Mirror mirror = new Mirror(); //The memory address remains unchanged. Even if the object of this class is re established, the address of the object remains unchanged
    int id;
    String name;

    makeUp(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public void makeUp() throws InterruptedException {
        if (this.id == 0) {
            synchronized (mirror) {         //Get the mirror and monopolize the mirror resources
                System.out.println(this.name + "Got the mirror");
                Thread.sleep(1000);      //Blocking this thread and letting another thread enter will not release the lock

            }
            synchronized (lipsTick) {  //I also want lipstick
                System.out.println(this.name + "Got lipstick");
            }
        } else {
            synchronized (lipsTick) { //Enter the second thread, obtain lipstick and monopolize the lipstick resource
                System.out.println(this.name + "Got lipstick");
                Thread.sleep(1000);   //Block this thread and let another thread enter
            }
            synchronized (mirror) {
                System.out.println(this.name + "Got the mirror");
            }
        }
    }

    public void run() {
        try {
            makeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        makeUp m1 = new makeUp(0, "Cinderella");
        makeUp m2 = new makeUp(1, "Snow White");
        m1.start();
        m2.start();
    }
}

Operation results:

It can be seen that the program is running normally Thread 1 did not release the lock before modification, because a synchronization block is nested in the synchronization block, and the outer synchronization block does not end, so lock 1 cannot be released After modification, each synchronization block is separated without nesting, so condition 2 for deadlock formation is prevented, so deadlock is not formed

Topics: Java lock