Example of hen laying eggs: multi-threaded communication producer and consumer wait/notify and condition/await/signal condition queue

table of Contents

 

Introduction

One-to-one production and consumption: a hen and a training

wait/notify

Lock condition queue

Many-to-many production and consumption: 2 hens and called Lian/Lian's wife

wait/notifyAll

Lock condition queue

Comparison of Lock and synchronized

to sum up


Introduction


Multi-threaded communication has always been a test site for high-frequency interviews . Some interviewers may ask the producer/consumer code to be handwritten on site to examine the skills of multi-threading. Today, we analyze the implementation process with the code in the case of hen laying eggs in real life. The hen lays eggs in the henhouse, and the hen lays the eggs from the henhouse. The hen lays the eggs in the henhouse. The producer is the producer. The hen picks up the eggs, and the consumer is the thread. In the producer and consumer model, the chicken coop is a container for eggs. In reality, there are many such cases, such as calling a number in a hospital. Let's draw a picture below.

image.png

 

 

One-to-one production and consumption: a hen and a training


wait/notify

package com.duyang.thread.basic.waitLock.demo;

import java.util.ArrayList;
import java.util.List;

/**
 * @author :jiaolian
 * @date :Created in 2020-12-30 16:18
 * @description:母鸡下蛋:一对一生产者和消费者
 * @modified By:
 * 公众号:叫练
 */
public class SingleNotifyWait {

    //装鸡蛋的容器
    private static class EggsList {
        private static final List<String> LIST = new ArrayList();
    }

    //生产者:母鸡实体类
    private static class HEN {
        private String name;

        public HEN(String name) {
            this.name = name;
        }

        //下蛋
        public void proEggs() throws InterruptedException {
            synchronized (EggsList.class) {
                if (EggsList.LIST.size() == 1) {
                    EggsList.class.wait();
                }
                //容器添加一个蛋
                EggsList.LIST.add("1");
                //鸡下蛋需要休息才能继续产蛋
                Thread.sleep(1000);
                System.out.println(name+":下了一个鸡蛋!");
                //通知叫练捡蛋
                EggsList.class.notify();
            }
        }
    }

    //人对象
    private static class Person {
        private String name;

        public Person(String name) {
            this.name = name;
        }
        //取蛋
        public void getEggs() throws InterruptedException {
            synchronized (EggsList.class) {
                if (EggsList.LIST.size() == 0) {
                    EggsList.class.wait();
                }
                Thread.sleep(500);
                EggsList.LIST.remove(0);
                System.out.println(name+":从容器中捡出一个鸡蛋");
                //通知叫练捡蛋
                EggsList.class.notify();
            }
        }
    }

    public static void main(String[] args) {
        //创造一个人和一只鸡
        HEN hen = new HEN("小黑");
        Person person = new Person("叫练");
        //创建线程执行下蛋和捡蛋的过程;
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    hen.proEggs();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        //叫练捡鸡蛋的过程!
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    person.getEggs();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

As in the above code, we define the EggsList class to hold eggs, the HEN class represents the hen, and the Person class represents the person. In the main function, create the hen object "Xiao Hei" and the human object "calling practice", and create two threads to execute the egg laying and egg picking processes respectively. It is defined in the code that only one egg can be placed in the chicken coop (of course, more than one can be defined). Detailed process: the "little black" hen thread competes with the "calling" thread for the lock. If the "little black" hen thread acquires the lock first and finds that the number of eggs in EggsList is greater than 0, it means there are eggs, then call wait to wait And release the lock to the "calling practice" thread. If there is no egg, call EggsList.LIST.add("1") to indicate that an egg is produced and notify the "calling practice" to fetch the eggs and release the lock for the "calling practice" thread to obtain lock. After the "calling practice" thread calls the getEggs() method to obtain the lock, it is found that if there are no eggs in the chicken coop, it calls wait to wait and release the lock to notify the "Xiao Hei" thread to acquire the lock and lay the eggs. If there are eggs, it means "Xiao Hei" has been When the egg is laid, take the egg away, because there are no eggs in the chicken coop, so the notify() method must be called at the end to notify "Xiao Hei" to lay the egg. We observe the execution result of the program as shown in the figure below. The two threads are in an infinite loop and the program will continue to execute. The lock used in the process of laying eggs and picking eggs is the class of the EggsList class. The competition between "Xiaohei" and "Calling Lian" is a unified lock, so this is Synchronous. This is how the hen "Xiao Hei" and "Jiao Lian" communicate.

image.png

What? ? ? Chickens and people can communicate! !

image.png

Lock condition queue

package com.duyang.thread.basic.waitLock.demo;

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

/**
 * @author :jiaolian
 * @date :Created in 2020-12-30 16:18
 * @description:母鸡下蛋:一对一生产者和消费者 条件队列
 * @modified By:
 * 公众号:叫练
 */
public class SingleCondition {

    private static Lock lock = new ReentrantLock();
    //条件队列
    private static Condition condition = lock.newCondition();

    //装鸡蛋的容器
    private static class EggsList {
        private static final List<String> LIST = new ArrayList();
    }

    //生产者:母鸡实体类
    private static class HEN {
        private String name;

        public HEN(String name) {
            this.name = name;
        }

        //下蛋
        public void proEggs() {
            try {
                lock.lock();
                if (EggsList.LIST.size() == 1) {
                    condition.await();
                }
                //容器添加一个蛋
                EggsList.LIST.add("1");
                //鸡下蛋需要休息才能继续产蛋
                Thread.sleep(1000);
                System.out.println(name+":下了一个鸡蛋!");
                //通知叫练捡蛋
                condition.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    //人对象
    private static class Person {
        private String name;

        public Person(String name) {
            this.name = name;
        }
        //取蛋
        public void getEggs() {
            try {
                lock.lock();
                if (EggsList.LIST.size() == 0) {
                    condition.await();
                }
                Thread.sleep(500);
                EggsList.LIST.remove(0);
                System.out.println(name+":从容器中捡出一个鸡蛋");
                //通知叫练捡蛋
                condition.signal();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        //创造一个人和一只鸡
        HEN hen = new HEN("小黑");
        Person person = new Person("叫练");
        //创建线程执行下蛋和捡蛋的过程;
        new Thread(()->{
            for (int i=0; i<Integer.MAX_VALUE;i++) {
                hen.proEggs();
            }
        }).start();
        //叫练捡鸡蛋的过程!
        new Thread(()->{
            for (int i=0; i<Integer.MAX_VALUE;i++) {
                person.getEggs();
            }
        }).start();
    }
}

As in the above code, only the synchronized is changed to Lock, the result of the program running is the same as the above, and the wait/notify is replaced with the AQS condition queue Condition to control the communication between threads. Lock needs to be manually locked with lock.lock(), and the step of unlocking lock.unlock() is placed in the finally code block to ensure that the lock can always be released. The bottom layer of await is realized by unsafe.park(false,0) calling C++ code.

 

Many-to-many production and consumption: 2 hens and called Lian/Lian's wife


wait/notifyAll

package com.duyang.thread.basic.waitLock.demo;

import java.util.ArrayList;
import java.util.List;

/**
 * @author :jiaolian
 * @date :Created in 2020-12-30 16:18
 * @description:母鸡下蛋:多对多生产者和消费者
 * @modified By:
 * 公众号:叫练
 */
public class MultNotifyWait {

    //装鸡蛋的容器
    private static class EggsList {
        private static final List<String> LIST = new ArrayList();
    }

    //生产者:母鸡实体类
    private static class HEN {
        private String name;

        public HEN(String name) {
            this.name = name;
        }

        //下蛋
        public void proEggs() throws InterruptedException {
            synchronized (EggsList.class) {
                while (EggsList.LIST.size() >= 10) {
                    EggsList.class.wait();
                }
                //容器添加一个蛋
                EggsList.LIST.add("1");
                //鸡下蛋需要休息才能继续产蛋
                Thread.sleep(1000);
                System.out.println(name+":下了一个鸡蛋!共有"+EggsList.LIST.size()+"个蛋");
                //通知叫练捡蛋
                EggsList.class.notify();
            }
        }
    }

    //人对象
    private static class Person {
        private String name;

        public Person(String name) {
            this.name = name;
        }
        //取蛋
        public void getEggs() throws InterruptedException {
            synchronized (EggsList.class) {
                while (EggsList.LIST.size() == 0) {
                    EggsList.class.wait();
                }
                Thread.sleep(500);
                EggsList.LIST.remove(0);
                System.out.println(name+":从容器中捡出一个鸡蛋!还剩"+EggsList.LIST.size()+"个蛋");
                //通知叫练捡蛋
                EggsList.class.notify();
            }
        }
    }

    public static void main(String[] args) {
        //创造一个人和一只鸡
        HEN hen1 = new HEN("小黑");
        HEN hen2 = new HEN("小黄");
        Person jiaolian = new Person("叫练");
        Person wife = new Person("叫练媳妇");
        //创建线程执行下蛋和捡蛋的过程;
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    hen1.proEggs();
                    Thread.sleep(50);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    hen2.proEggs();
                    Thread.sleep(50);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        //叫练捡鸡蛋的线程!
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    jiaolian.getEggs();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        //叫练媳妇捡鸡蛋的线程!
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    wife.getEggs();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

As in the above code, some modifications have been made with reference to the wait/notify code in one-to-one production and consumption. Two hen threads "Xiao Hei" and "Xiao Huang" are created, and two egg-picking threads are called "Lian", " Called "Lian's Wife", the execution results are synchronized, and many-to-many production and consumption are realized, as shown in the figure below. There are the following points to note:

  1. The largest egg in a henhouse is 10.
  2. In the proEggs() method to determine whether the number of eggs is greater than or equal to 10, the while loop is used. Wait receives the notification and wakes up the current thread. It needs to be judged again to avoid logic problems in the program. You cannot use if here. If you use if, the program It may happen that the EggsList has more than 10 eggs. This is where errors are prone to occur in this program, and it is also a point that is often asked. It is worth investigating.
  3. Many-to-many producers and consumers.

image.png

Lock condition queue


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

/**
 * @author :jiaolian
 * @date :Created in 2020-12-30 16:18
 * @description:母鸡下蛋:多对多生产者和消费者 条件队列
 * @modified By:
 * 公众号:叫练
 */
public class MultCondition {

    private static Lock lock = new ReentrantLock();
    //条件队列
    private static Condition condition = lock.newCondition();

    //装鸡蛋的容器
    private static class EggsList {
        private static final List<String> LIST = new ArrayList();
    }

    //生产者:母鸡实体类
    private static class HEN {
        private String name;

        public HEN(String name) {
            this.name = name;
        }

        //下蛋
        public void proEggs() {
            try {
                lock.lock();
                while (EggsList.LIST.size() >= 10) {
                    condition.await();
                }
                //容器添加一个蛋
                EggsList.LIST.add("1");
                //鸡下蛋需要休息才能继续产蛋
                Thread.sleep(1000);
                System.out.println(name+":下了一个鸡蛋!共有"+ EggsList.LIST.size()+"个蛋");
                //通知叫练/叫练媳妇捡蛋
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    //人对象
    private static class Person {
        private String name;

        public Person(String name) {
            this.name = name;
        }
        //取蛋
        public void getEggs() throws InterruptedException {
            try {
                lock.lock();
                while (EggsList.LIST.size() == 0) {
                    condition.await();
                }
                Thread.sleep(500);
                EggsList.LIST.remove(0);
                System.out.println(name+":从容器中捡出一个鸡蛋!还剩"+ EggsList.LIST.size()+"个蛋");
                //通知叫练捡蛋
                condition.signalAll();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        //创造一个人和一只鸡
        HEN hen1 = new HEN("小黑");
        HEN hen2 = new HEN("小黄");
        Person jiaolian = new Person("叫练");
        Person wife = new Person("叫练媳妇");
        //创建线程执行下蛋和捡蛋的过程;
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    hen1.proEggs();
                    Thread.sleep(50);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    hen2.proEggs();
                    Thread.sleep(50);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        //叫练捡鸡蛋的线程!
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    jiaolian.getEggs();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        //叫练媳妇捡鸡蛋的线程!
        new Thread(()->{
            try {
                for (int i=0; i<Integer.MAX_VALUE;i++) {
                    wife.getEggs();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

As in the above code, just replace synchronized with Lock, the result of program operation is the same as above, let's compare the similarities and differences between Lock and synchronized below. This question is often asked during interviews!

 

Comparison of Lock and synchronized


Both Lock and synchronized can synchronize multiple threads. The main similarities and differences are as follows!

  1. Lock nature: Lock optimistic lock is non-blocking, the bottom layer is implemented by cas+volatile, synchronized pessimistic lock is blocking and requires context switching. Realize the idea differently.
  2. Functional details: Lock needs to be manually unlocked, synchronized is automatically unlocked. Lock also provides more granular functions, such as tryLock.
  3. Thread communication: Lock provides a Condition queue, one lock can correspond to multiple condition queues, and the thread control is more delicate. Synchronized can only correspond to one wait/notify.

That's it. If you don't know much about synchronized, volatile, and cas keywords, you can read my previous article for detailed cases and explanations.

 

to sum up


Today, I used the examples in life to convert into code, and realized two multi-threaded consumer/producer modes. The suggestion for you is to type the code again. If you execute the code carefully, you should be able to understand it. If you like it, please click Like and follow. My name is Lian [ Official Account ] , I call and practice.

image.png

Guess you like

Origin blog.csdn.net/duyabc/article/details/112002870