Multithreading (Basic Level 5: wait and notify)

Table of contents

1. Concept

2. Usage

(1) Give a chestnut:

(2) Use of wait and notify

1. No locked wait

2. When a thread is wait, but no other thread notify to release the wait

3. Two threads, one thread wait, and one thread notify to release wait

4、notifyAll

(3) Three options of wait

三、wait、sleep、join


1. Concept

We know that the scheduling of multi-threads in the system is random. We cannot interfere with the execution order of multiple threads, but we can make a thread give up being called by the system and let other threads be called first. In this way, we can achieve our expectations Effect;

When wait allows multiple threads to compete for locks, the thread that executes later gives up the lock competition with other threads. After other threads finish executing, other threads use notify to release the information that the thread in wait does not want to compete for locks. Dropped and competed with other thread locks again. Waiting and notification mechanism (similar to join)


2. Usage

(1) Give a chestnut:

Nowadays, there are many funny old people who want to go to the ATM. Funny A is for withdrawing money, Funny B is for depositing money, and Funny C is the person who transports banknotes. They are responsible for replenishing money to the ATM machine to prevent the ATM machine from running out of money and others being unable to withdraw it. Get money. We treat Funny A, Funny B, and Funny C here as threads. Every time we go to the ATM machine, only one person can enter, which is equivalent to being locked. Others cannot enter. After the people in the ATM machine complete the operation, After coming out, others can enter, but this is due to multi-threading, and other threads will compete for locks.​​     

When A enters the ATM machine, it is locked and no one else can enter, as shown in the picture:

Compare Funny A to a thread. When thread A enters, it will be locked. Thread A needs to perform the operation of withdrawing money. Other threads cannot enter and operate. When thread A completes its own operation, other threads can compete for the lock.

But if there is no money in the ATM machine, thread A cannot complete the operation of withdrawing money. It will exit the ATM machine. But after exiting, because it has not completed the operation of withdrawing money, it will want to continue to enter the ATM to complete the withdrawal of money. This operation will continue to compete with other threads for locks, because thread A has obtained the lock and is in the RUNNABLE state. Other threads are blocked and in the BLOCKED state. They need to be awakened by the system before they can compete for the lock. But thread A does not need to You can compete for the lock after waking up, and there is a high possibility that the lock will be obtained by thread A later (similar to the first-come-first-served approach). If this is the case, thread A will frequently go in and out, unable to do anything, but other threads will not be able to go in and operate. In layman's terms, it means occupying the manhole and not taking a shit. Thread safety issues arise. Other threads cannot obtain the lock. This situation is called "thread starvation".

The approximate logic of the thread A code here is as follows:

When thread A does not get the money, it will keep repeating the locking and unlocking operations.

Such bugs are not as serious as deadlocks, but they still need to be solved. So how to solve it. At this time, you can use wait and notify. It is expected to be improved as shown below:

The wait here does three things internally:

(1) Release the lock and let other threads compete for the lock

(2) Enter blocking wait

(3) After other threads use notify, release wait and participate in lock competition.

(2) Use of wait and notify

The prerequisite for using wait must be that the current object is locked before it can be used. If you wait if the object is not locked, you don't know who you are waiting for. At the same time, if a thread waits, other threads must notify to release the wait, otherwise the wait will remain blocked.

1. No locked wait

Code:

public class TestDemo1 {
    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();
        locker.wait();
    }
}

Results of the:

2. When a thread is wait, but no other thread notify to release the wait

Code:

public class TestDemo3 {
    public static void main(String[] args) {
        Object locker = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("wait之前");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait之后");
            }
        });
        t1.start();
    }
}

Results of the:

Cannot print "after wait", it is always in blocking waiting state. In jconsole, the status is as shown in the figure

3. Two threads, one thread wait, and one thread notify to release wait

Code:

public class TestDemo2 {
    public static void main(String[] args) {
        Object locker = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("t1 wait之前");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("t2 wait之后");
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (locker) {
                System.out.println("t2 notify之前");
                locker.notify();
                System.out.println("t2 notify之后");
            }
        });
        t1.start();
        t2.start();
    }
}

Note: The objects used for notify to release wait must be the same. If they are different, wait cannot be released and t1 cannot be awakened. .

In the system, notify does not need to be locked, but in Java, it is stipulated that it must be locked, and the locked object must be the same as the notify object, so it is different from the system.

Results of the:

Result analysis:

When t1 and t2 are executed

(1) Because t1 sleeps for 1 second, it is guaranteed that t1 waits first, so "t1 wait before" is printed first. At this time, t1 enters the blocking waiting state.

(2) After the t2 thread sleeps for 1 second, it acquires the locker lock and prints "t2 notify before". When the t2 thread executes notify, the wait of the t1 thread is released.

(3) Because t2 is still holding the lock, t1 will still enter blocking, and t2 prints "t2 notify" to release the lock.

(4) t1 gets the lock and then prints "t1 wait".

4、notifyAll

Wake up all threads waiting for this object; Assume there are many threads, all using the same object wait. At this time, use notifyAll, all threads using the wait of this object , will be awakened.

Note:When these threads are awakened, they must reacquire the lock, and they still have to compete for the lock. This is equivalent to serial execution (thread scheduling) Still scheduled randomly). Moreover, after using notifyAll, all threads using the same object wait will be awakened, which is difficult to control. It is more recommended to use notify.

(3) Three options of wait

As shown in the picture:

If there are no parameters, it means waiting, but in many cases, waiting is unreasonable, so we add parameters to make a thread wait for a certain time. If this time is exceeded, it will not wait and just skip the wait.

One parameter has an accurate range of milliseconds, and two parameters have an accurate range of nanoseconds.


三、wait、sleep、join

wait: needs to be used with synchronized. When the thread waits, it is in the WAITING state. It needs to be notified by other threads before it can be awakened, or it can set the time and wake up at that time. You can know the details .

sleep: When the thread sleeps, it cannot be awakened until a certain sleep time, but it can also be terminated by interrupt. However, in this case, an exception will be thrown, which is an unconventional method. Doesn't meet our expectations.

join: Whichever thread calls join, the current thread will have to wait for that thread to finish executing before it can become the current thread; just like wait, there are parameters to choose from, and it will not wait until then. .

Guess you like

Origin blog.csdn.net/cool_tao6/article/details/134735138