The introduction

This is something THAT occurred to me when I was looking at multithreaded communication today, why inter-thread communications like wait(), notify(), and notifyAll() need to be in a synchronized block. What if the wait() method is not in the synchronized block?

@Test
public void test(a) {
    try {
        new Object().wait();
    } catch(InterruptedException e) { e.printStackTrace(); }}Copy the code

The result is:

After a bit of Googling and looking at the blogs of various celebrities, I finally found the answer.

Lost Wake-Up Problem

Let’s start with an example of a consumer thread and a producer thread. The producer’s task is count+1, and then wakes up the consumer; The consumer’s task is count-1 and falls asleep when the count is 0.

Producer pseudocode:

count++;
notify();
Copy the code

Consumer pseudocode:

while(count <= 0){
    wait(a); count--; }Copy the code

Anyone familiar with multithreading should see at a glance what happens if the producer and consumer steps are mixed.

If count = 0, the customer checks the value of count and finds that count <= 0. At this point, a context switch occurs, the producer comes in, crackles, completes both steps, and sends a notification that it is ready to wake up a thread. At this point the consumer has just decided to go to sleep and hasn’t gone to sleep yet, so the notice will be thrown away. Next thing you know, the consumer goes to sleep…

Why does wait() do this

This is called the Lost wakeup Problem

How to solve

By now we should be able to see that the root of the problem is that between the time the consumer checks the count and calls wait(), the count may have been changed. So how do we solve this? Let the consumer and producer compete for a lock, and only when they compete can they change the count value.

Hence the producer code:

lock();
count++;
notify();
unlock();
Copy the code

Consumer Code:

lock();
while(count <= 0){
    wait(a); count--; } unlock();Copy the code

Now let’s see, does that really work out?

The answer is that it doesn’t work. The lost wake up problem is the same as the unlocked one.

This is because wait() releases the lock and then waits for it to be acquired.

The ultimate answer

Therefore, we can conclude that in order to avoid this lost wake up problem, we should always put our code in the synchronization block under this model.

Java forces our wait()/notify() calls to be in a synchronized block, just so we don’t accidentally get lost wake up.

Not just these two methods, including Java. Util. Concurrent. The locks. The Condition of await ()/signal () must be in a synchronized block.

Truth:

private Object obj = new Object();
private Object anotherObj = new Object();

@Test
public void produce(a) {
    synchronized (obj) {
        try {
            // The synchronized block is responsible for the current thread, not anotherobj.notify ();
            obj.notify();
        } catch(Exception e) { e.printStackTrace(); }}}Copy the code

The use of wait and notify

Wait (), notify(), and notifyAll()

  • The wait(), notify(), and notifyAll() methods are local and final and cannot be overridden.

  • Calling the wait() method on an object causes the current thread to block, and the current thread must own the object’s monitor (that is, the lock, or pipe).

  • Calling the notify() method on an object can wake up a thread that is waiting for the object’s monitor. If more than one thread is waiting for the object’s monitor, only one thread can be woken up.

  • Calling notifyAll() wakes up all monitor threads that are waiting for the object.

The specific application


/** * wait() && notify() methods * These two methods are defined in Object and are used to coordinate thread synchronization, which is more flexible than join */
public class NotifyDemo {
    public static void main(String[] args) {
        // Write two threads
        Object obj=new Object();
        Thread download=new Thread(){
            public void run(a) {
                System.out.println("Start downloading pictures");
                for (int i = 0; i < 101; i+=10) {
                    System.out.println("down"+i+"%");
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("Image downloaded successfully");
                synchronized (obj) {
                    obj.notify();/ / call
                }
                System.out.println("Start downloading attachments");
                for (int i = 0; i < 101; i+=10) {
                    System.out.println("Attachment download"+i+"%");

                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
                System.out.println("Attachment downloaded successfully"); }};//2
        Thread show=new Thread(){
            public void run(a){
                synchronized (obj) {
                    try {
                        obj.wait();// Block the current
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("Show: Start showing pictures.");
                    System.out.println("Picture shown."); }}}; download.start(); show.start(); }}Copy the code