Java EE - Multithreading 4

1. Deadlock Definition

Deadlock is a phenomenon in which two or more execution units (processes, threads or coroutines) are blocked due to competition for resources or due to communication with each other during the execution process. If there is no external force, they will all be blocked. Unable to move forward.

1.1 Causes of deadlock

1.1.1 Mutual exclusion conditions

A resource (lock) can only be occupied by one thread at a time, and when the resource is occupied, other threads can only wait.

1.1.2 Inalienable Conditions

When a resource is occupied, if it is not released by the thread that owns the resource, other threads cannot get the resource.

1.1.3 Request and hold

When a thread has a certain resource, it is not satisfied, and it is requesting other resources.

1.1.4 LOOP WAIT CONDITIONS

In the case of multiple threads requesting resources, a loop chain is formed.

To form a deadlock, the above four conditions must be met, all of which are indispensable.

1.2 How to solve deadlock

It is enough to break one or more conditions that form a deadlock.

1.2.1 Analysis

  1. Mutually exclusive conditions -> unchangeable
  2. inalienable condition -> unchangeable
  3. request and hold condition -> human control can be broken can be modified
  4. Loop wait conditions -> artificial control can be broken and can be broken
    loop wait conditions -> use sequential locks

2. Thread communication

Since threads are executed preemptively, the sequence of threads is unpredictable.
But in actual development, sometimes we want to reasonably coordinate the execution sequence of multiple threads.

2.1 Method introduction

Completing this coordination work (thread communication) mainly involves three methods.
Note: All need to be used with synchronized Object level
Reason: 1. JVM level regulations.
2. When running, it will judge whether these three methods are used with synchronized. If they are not used with synchronized, an error will be reported. It is designed to prevent thread confusion caused by locks when multiple threads communicate with each other.

2.1.1 Using wait

wait() / wait(long timeout) : let the current thread enter the waiting state

wait execution process:
make the thread currently executing the code wait (put the thread in the waiting queue) to
release the current lock
, wake up when certain conditions are met, and try to acquire the lock again.
Note: wait should be used with synchronized, and using wait without synchronized will throw an exception directly.
wait ends the waiting condition:
other threads call the notify method of the object.
wait wait timeout
Other threads call the interrupted method of the waiting thread, causing wait to throw interruptedException.

public class WaitDemo {
    
    
    public static void main(String[] args) {
    
    
        Object lock = new Object();

        Thread t1 = new Thread(()->{
    
    
            System.out.println("线程1开始执行");

            try {
    
    
                synchronized (lock) {
    
    
                    System.out.println("线程1调用wait方法");
                    // 无限期的等待状态
                    lock.wait();
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("线程1执行完成");

        },"线程1");
        t1.start();
    }
}

2.1.2 Using notify

notify() : Randomly wake up the thread that enters the sleep state (randomly)
After the notify() call, it does not immediately wake up the thread to start executing, but waits for the synchronized in notify to finish executing (release the lock) before it can really wake up and start executing

public class notifyDemo2 {
    
    
    public static void main(String[] args) {
    
    
        Object lock = new Object();
        Object lock2 = new Object();

        Thread t1 = new Thread(()->{
    
    
            System.out.println("线程1开始执行");

            try {
    
    
                synchronized (lock) {
    
    
                    System.out.println("线程1调用wait方法");
                    // 无限期的等待状态
                    lock.wait();
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("线程1执行完成");

        },"线程1");
        t1.start();

        Thread t2 = new Thread(()->{
    
    
            System.out.println("线程2开始执行");

            try {
    
    
                synchronized (lock) {
    
    
                    System.out.println("线程2调用wait方法");
                    // 无限期的等待状态
                    lock.wait();
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("线程2执行完成");

        },"线程2");
        t2.start();

        Thread t3 = new Thread(()->{
    
    
            System.out.println("线程3开始执行");

            try {
    
    
                synchronized (lock2) {
    
    
                    System.out.println("线程3调用wait方法");
                    // 无限期的等待状态
                    lock2.wait();
                }
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("线程3执行完成");

        },"线程3");
        t3.start();

        // 唤醒lock 对象上休眠的线程
        Thread t4 = new Thread(()->{
    
    
            try {
    
    
                Thread.sleep(500);
            } catch (InterruptedException e) {
    
    
            }
            System.out.println("线程4:开始执行,唤醒线程");
            synchronized (lock){
    
    
                lock.notify();
                System.out.println("线程4:执行了唤醒操作");
            }
        },"线程4");
        t4.start();
    }
}

2.1.3 use of notiyAll

notifyAll(): Wake up all sleeping threads.

2.2 Notes

  1. wait /notify /notifyAll must be executed together with synchronized.
  2. wait /notify /notifyAll for synchronized locking, be sure to use the same object for locking.
  3. When wait /notify /notifyAll is called, the program will not resume execution immediately, but will try to acquire the lock, and will continue to execute only after the lock is obtained.
  4. notifyAll does not wake up all wait waiting threads, but wakes up all threads that the current object is waiting for.

2.3 The difference between wait with and without parameters

difference:

  1. wait(long timeout) : When the thread exceeds the set time, it will automatically resume execution;
    wait() wireless waiting state
  2. Using the wait method without parameters, the thread will enter WAITING;
    using the wait method with parameters, the thread will enter TIMED_WAITING. The
    same point:
  3. Either the wait method with parameters or the wait method without parameters can make the current thread go to sleep
  4. Either the wait method with parameters or the wait method without parameters can use notify or notifyAll to wake up.

2.3.1 Difference between wait(0) and sleep(0)

wait(0) : wait indefinitely
sleep(0) :

import java.util.concurrent.TimeUnit;

public class WaitSleepDemo7 {
    
    
    public static void main(String[] args) throws InterruptedException {
    
    
        Object lock = new Object();
        Thread t1 = new Thread(()->{
    
    
            synchronized (lock) {
    
    
                System.out.println("线程1:开始执行");
                try {
    
    
                    lock.wait(0);
                } catch (InterruptedException e) {
    
    
                    e.printStackTrace();
                }
                System.out.println("线程1:结束执行");
            }
        },"wait0");
        t1.start();

        Thread t2 = new Thread(()->{
    
    
            System.out.println("线程2:开始执行");
            try {
    
    
                Thread.sleep(0);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("线程2:执行结束");
        },"sleep0");
        t2.start();
    }
}

Guess you like

Origin blog.csdn.net/biteqq/article/details/123922660