Java multithreading producer consumer -- wait && notify & & false wake-up

Java multithreading (VII) producer consumer -- wait & & notify & & false wake-up

Producer consumer issues

Producer consumer problem is a classic multi-threaded synchronization problem. For example, there are two threads a and B, which share a fixed size buffer. Thread a generates data and puts it into the buffer. Thread B takes data from the buffer for calculation. In fact, this is a producer consumer model. A is equivalent to producer and B is equivalent to consumer.

It is necessary to raise the producer consumer problem. For example, if A is A producer and the capacity of the container (buffer or array) loaded by him is only 10, the producer consumer problem has two limitations:

  1. The number of existing products produced by the producer should be less than or equal to 10. If the number of products reaches 10, the producer cannot work;
  2. When consumers consume, the number of existing products should be greater than 0. If there are no products (0), consumers cannot work;

wait() and notify()

1. The function of wait() is to make the current thread enter the waiting state. At the same time, wait() will also make the current thread release the lock it holds. "Until other threads call the notify() method or notifyAll() method of this object", the current thread is awakened (enters the "ready state")

2. The function of notify() and notifyAll() is to wake up the waiting thread on the current object; Notify () wakes up a single thread, while notifyAll () wakes up all threads.

What's the difference between wait() and sleep()

  1. The two methods come from different classes. The sleep method comes from the thread class, but the wait method comes from the Object class. Sleep is a static class method of Thread. If anyone calls to sleep, even if the sleep method of b is invoked in the a thread, a is actually going to hibernate. Let the b thread sleep to invoke sleep in the b code.
  2. The sleep method does not release the lock, while the wait method releases the lock so that other threads can use the synchronization control block or method. Sleep does not transfer system resources; Wait is to enter the thread waiting pool to wait, transfer system resources, and other threads can occupy CPU. Wait for other threads to call notify/notifyAll to wake up all threads in the waiting pool before entering the ready queue and waiting for the OS to allocate system resources.
  3. Scope of use: wait, notify and notifyAll can only be used in synchronization control methods or synchronization control blocks, while sleep can be used anywhere.
  4. sleep must catch exceptions, while wait, notify and notifyAll do not.

Realizing producer consumer issues

Now there is a producer who can produce tickets, but the existing tickets can only be one at most. As long as there are tickets, consumers can get them.

public class PCold {

    public static void main(String[] args) {
        tickets t = new tickets();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    t.product();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    t.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}

class tickets{
    private int num = 0;
    public synchronized void product() throws InterruptedException {
        if(num!=0)
        {
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+" Produced one  "+num);
        this.notifyAll();
    }

    public synchronized void consume() throws InterruptedException {
        if(num!=1)
        {
            this.wait();

        }
        num--;
        System.out.println(Thread.currentThread().getName()+" Consumed one  "+num);
        this.notifyAll();
    }
}

Code problem - false wake-up

The above results are no problem, but in fact, if you increase the number of threads, there will be a problem. If you change the number of producer threads to two and the number of consumers to two, there will be a problem. (just increase the number of threads in the main function)

public class PCold {

    public static void main(String[] args) {
        tickets t = new tickets();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    t.product();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    t.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    t.product();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();


        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    t.consume();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}

The above problem is due to false awakening. False wake-up means that in the process of multithreading, the communication between threads does not wake up in the order we imagined, so the data inconsistency does not meet our expected results. The problem of false wake-up is in the if statement. We can analyze:

When A produces A product and uses the notifyAll function to wake up the other three threads, for C, he calls the product function. Before being awakened by A, he is stuck in the wait() function. After being awakened, because it is in the if statement block, it is no longer necessary to judge whether the number of votes is equal to 0, so he directly executes the num + + statement, Therefore, the problem of false wake-up is caused. The solution is very simple. You only need to replace the if statement with the while statement. After being awakened, let him judge the number of num again, and then decide whether to execute the operation of num + +.

public synchronized void product() throws InterruptedException {
        if(num!=0)
        {
            this.wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName()+" Produced one,The quantity on hand is "+num);
        this.notifyAll();
    }

(you only need to change the if in the product and consume r functions to while, so you don't need to put the code, just put the result)

Now, the problem of inconsistent data has been solved, but another problem is that their order is not very good. It's best to pass a - > b - > C - > D. then the Condition operation needs to be used. You can read this article to understand the usage of Condition.

Tags: Java

Posted on Sat, 25 Sep 2021 21:02:12 -0400 by toro04