Java multithreading - Lock (deadlock, Lock)

Posted by ju8ular1 on Sun, 26 Sep 2021 02:54:28 +0200

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 problem of "deadlock" may occur.

Deadlock: multiple threads hold each other's required resources, and then form a deadlock

For example: two girls need lipstick and a mirror for makeup (both have only one), and one wants to take lipstick first and then the mirror; The other wants to take the mirror first and then the lipstick

// Deadlock: multiple threads hold each other's required resources, and then form a deadlock
public class DeadLockDemo {
    public static void main(String[] args) {
        MakeUp m1 = new MakeUp(0, "Zhang San");
        MakeUp m2 = new MakeUp(1, "Li Si");

        m1.start();
        m2.start();
    }
}

// Lipstick
class Lipstick {

}

// mirror
class Mirror {

}

// Make up
class MakeUp extends Thread {
    // Only one resource is needed, which is guaranteed by static
    static Lipstick lipstick = new Lipstick();
    static Mirror mipstick = new Mirror();

    // choice
    int choice;
    // People who use cosmetics
    String girlName;

    public MakeUp(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {
            doMakeUp();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // Makeup, holding each other's locks, is to get each other's resources
    // Suppose two people, one comes first, want to take lipstick first, and then take the mirror; The other came in and wanted to get the mirror first and then lipstick
    private void doMakeUp() throws InterruptedException {
        if (choice == 0) {
            // Get lipstick lock
            synchronized (lipstick) {
                System.out.println(this.girlName + "Get lipstick lock");
                Thread.sleep(1000);
                // I want to get the mirror in a second
                synchronized (mipstick) {
                    System.out.println(this.girlName + "Get the lock of the mirror");
                }
            }
        } else {
            // Get the lock of the mirror
            synchronized (mipstick) {
                System.out.println(this.girlName + "Get the lock of the mirror");
                Thread.sleep(2000);
                // Want to get lipstick in a second
                synchronized (lipstick) {
                    System.out.println(this.girlName + "Get lipstick lock");
                }
            }
        }
    }
}

Running result: deadlock occurs and the program does not move

Solution: don't let two people hold one lock (the other's lock) at the same time

Four necessary 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 will hold 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

Lock

Since JDK 1.5, Java has provided a more powerful thread synchronization mechanism -- synchronization is achieved by explicitly defining synchronization Lock objects. Synchronous locks use Lock objects as.

The java.uti.concurrent.locks.Lock interface is a tool that controls multiple threads to access shared resources.
Locks provide exclusive access to shared resources. Only one thread can Lock the Lock object at a time. Threads should obtain the Lock object before accessing shared resources.

Reentrantlock class implements Lock. It has the same concurrency and memory semantics as synchronized. Reentrantlock is commonly used in thread safety control, which can explicitly add and release locks.

Example: multiple resources operate on the same object

public class LockDemo {
    public static void main(String[] args) {
        Lock2 lock2 = new Lock2();
        new Thread(lock2).start();
        new Thread(lock2).start();
        new Thread(lock2).start();
    }
}

class Lock2 implements Runnable {
    int num = 10;

    @Override
    public void run() {
        while (true) {
            if (num > 0) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(num--);
            } else {
                break;
            }
        }
    }
}

- 1 is output in the result, and the thread is unsafe

Add lock:

synchronized vs Lock

  • Lock is an explicit lock (manually open and close the lock, don't forget to close the lock), and synchronized is an implicit lock, which is automatically released out of the scope
  • Lock only has code block lock, and synchronized has code block lock and method lock
  • Using Lock lock, the JVM will spend less time scheduling threads and perform better. And it has better extensibility (providing more subclasses)
  • Priority:
    • Lock > synchronize code block (it has entered the method body and allocated corresponding resources) > synchronize method (outside the method body)

Topics: Java Multithreading