5-2 application of blocking queue - producer consumer

Keywords: Java JUC

1 false Awakening

What is false awakening?

Refer to a question and answer on the Internet:

Question: what are the reasons for false wakeup in java multithreading?

The definition found on the Internet means that the thread wakes up without calling notify() and notifyAll(), which is a false wake-up. What causes this?

Answer: spurious wakeup is an appearance, that is, in a multiprocessor system, the program that sends a wait may wake up and continue to execute without notify wakeup. Taking the java program running on the linux hotspot virtual machine as an example, the wait method essentially calls the underlying pthread when the jvm executes_ cond_ wait/pthread_ cond_ The timedwait function suspends the wait condition variable to achieve the effect of synchronous communication between threads. At the beginning of design, in order not to slow down the efficiency of condition variable operation, the underlying wait function did not guarantee that each wake-up was triggered by notify, but entrusted this task to the upper application, That is, the user needs to define a loop to judge whether the conditions can really meet the needs of the program to continue running. Of course, this implementation can also avoid the problem of abnormal wake-up of the program due to design defects.

Let's reproduce the scene

class ProConShop{
    int num;
    /**
     * The clerk put the goods on the shelf
     */
    public synchronized void incrementProduct() throws InterruptedException {
        //1. Judge whether there are goods in the store
        if (num!=0){//spurious wakeup 
       //while (num!=0){
            this.wait();
        }
        //2 production goods
        num++;
        System.out.println(Thread.currentThread().getName()+"Put in the item with the item No"+num);
        //3 notify consumers
        this.notifyAll();
    }

    /**
     * Consumer goods
     */
    public synchronized void decrementProduct() throws InterruptedException {
        //1. Judge whether there are goods in the store
        if (num==0){
            //while (num==0){
            this.wait();
        }
        //2 consumer goods
        System.out.println(Thread.currentThread().getName()+"Customer No. purchased commodity, commodity No"+num);
        num--;
        //3 notify the producer
        this.notifyAll();
    }
}

public class ProductTest {
    public static void main(String[] args) {
        ProConShop shop = new ProConShop();
        for (int i = 1; i <= 5; i++) {
            new Thread(()->{
                try {
                    shop.incrementProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"clerk").start();
        }
        for (int i = 1; i <= 5; i++) {
            new Thread(()->{
                try {
                    shop.decrementProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },i+"").start();
        }
    }
}

From this place num= 0 thread has no wait

This article 00 multithreading 5.2 thread communication case: the producer consumer code has a false wake-up problem

An interpretation from the Internet

Solution: replace the producer consumer method judgment statement if with while

The operation effect is:

2 producer consumer case Lock mode

Producer consumer case synchronized implementation   1 false Awakening  

2.1 use lock instead of synchronized

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

package com.nie.juc;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class ProConShop{

    int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    /**
     * The clerk put the goods on the shelf
     */
    public void incrementProduct() throws InterruptedException {
        lock.lock();
        try {
            //1. Judge whether there are goods in the store
            while (num!=0){
                this.wait();
//                condition.await();// wait for
            }
            //2 production goods
            num++;
            System.out.println(Thread.currentThread().getName()+"Put in the item with the item No"+num);
            //3 notify consumers
            this.notifyAll();
//            condition.signalAll();// awaken
        }finally {
            lock.unlock();//Release lock
        }
    }

    /**
     * Consumer goods
     */
    public void decrementProduct() throws InterruptedException {
        lock.lock();
        try {
            //1. Judge whether there are goods in the store
            while (num==0){
                this.wait();
//                condition.await();// wait for
            }
            //2 consumer goods
            System.out.println(Thread.currentThread().getName()+"Customer No. purchased commodity, commodity No"+num);
            num--;
            //3 notify the producer
            this.notifyAll();
//            condition.signalAll();// awaken
        }finally {
            lock.unlock();//Release lock
        }
    }
}

public class ProductTest {
    public static void main(String[] args) {
        ProConShop shop = new ProConShop();
        for (int i = 1; i <= 5; i++) {
            new Thread(()->{
                try {
                    shop.incrementProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"Clerk 1").start();
        }
        for (int i = 1; i <= 5; i++) {
            new Thread(()->{
                try {
                    shop.decrementProduct();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"First customers"+i).start();
        }
    }
}

Run code error

  The solution is to replace the previous wait() and notifyAll() with wait wait() and wake signalAll() of Condition

2.2 precise notification sequence access

The advantage of using lock.Condition is that it can accurately wake up the threads you want to execute

For example: ① multiple threads are called in order to realize a - > b - > C ② traffic light scenario

Traffic light scene simulation code

package com.nie.juc;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * Simulated traffic lights
 */
class TrafficLight{
    Lock lock = new ReentrantLock();
    Condition green = lock.newCondition();//green light
    Condition yellow = lock.newCondition();//Yellow lamp
    Condition red = lock.newCondition();//red light
    //1-green  2-yellow 3-red
   volatile int state = 1;

    public void greenLight(){
        lock.lock();
        try {
            if (state!=1){ //Judge whether the light is green. If not, wait
                green.await();
            } else {
                System.out.println(Thread.currentThread().getName()+"\t pass at a green light=========");
                TimeUnit.SECONDS.sleep(1);
                //Notification yellow light
                state=2;
                yellow.signal();
//                green.signalAll(); // it won 't work
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void yellowLight(){
        lock.lock();
        try {
            if (state!=2){ //Judge whether it is yellow. If not, wait
                yellow.await();
            }else {
                System.out.println(Thread.currentThread().getName()+"\t Yellow light, wait a minute=========");
                TimeUnit.SECONDS.sleep(1);
                //Notification red light
                state=3;
                red.signal();
//                yellow.signalAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void redLight(){
        lock.lock();
        try {
            if (state!=3){ //Judge whether it is a red light. If not, wait
                red.await();
            }else {
                System.out.println(Thread.currentThread().getName()+"\t Stop at red light=========");
                TimeUnit.SECONDS.sleep(1);
                //Notification green light
                state=1;
                green.signal();
//                red.signalAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

public class TrafficLightDemo {
    public static void main(String[] args) {
        TrafficLight trafficLight = new TrafficLight();
        new Thread(()->{
            while (true) {
                trafficLight.greenLight();
            }
        }).start();
        new Thread(()->{
            while (true) {
                trafficLight.yellowLight();
            }
        }).start();
        new Thread(()->{
            while (true) {
                trafficLight.redLight();
            }
        }).start();
    }
}

The print result is:

 

Posted by T.Stedel on Fri, 29 Oct 2021 02:59:14 -0700