Three implementations of the producer consumer model

1. What is the producer consumer model:

The producer-consumer model uses a container to solve the problem of strong coupling between producers and consumers. Producers and consumers do not communicate directly with each other, but communicate through blocking queues. Therefore, after producers produce data, they do not need to wait for consumers to process it, but directly throw it to the blocking queue. Consumers do not ask producers for data, but Take it directly from the blocking queue, which is equivalent to a buffer, which balances the processing capabilities of producers and consumers .

This blocking queue is used to decouple producers and consumers. Looking at most design patterns, a third party will be found for decoupling. For example, the third party in the factory mode is the factory class, and the third party in the template mode is the template class. In the process of learning some design patterns, if we first find the third party of this pattern, it can help us quickly become familiar with a design pattern .
insert image description here

insert image description here

As shown in the figure above: when there is no data in the queue, all threads on the consumer end will be automatically blocked (suspended) until data is put into the queue.

insert image description here

As shown in the figure above: when the queue is full of data, all threads on the producer side will be automatically blocked (suspended) until there is an empty position in the queue, and the thread will be automatically awakened.

2. Implementation of the producer-consumer model :

The producer is a bunch of threads, and the consumer is another bunch of threads. The memory buffer can use the List array queue, and the data type only needs to define a simple class. The key is how to deal with the cooperation between multiple threads. This is actually an example of multi-threaded communication.

In this model, the most important thing is that the consumer must wait when the memory buffer is empty, and the producer must wait when the memory buffer is full. Other times it can be a dynamic balance . It is worth noting that when multi-threading operates on critical section resources (that is, shared resources), it must be guaranteed that only one thread can exist in reading and writing, so a lock strategy needs to be designed.




The first: use synchronized and wait, notify

package com.fan.blockqueue;
import java.util.concurrent.TimeUnit;
//使用 synchronized和wait、notify实现生产者和消费者
//1.定义资源类
class MyCacheResources1{
    int num = 0;//共享资源:生产和消费数字
    //资源池中实际存储的数据个数
    private int count = 0;
    //资源池中允许存放的资源数目
    private int capacity = 5;

    Object obj= new Object();//作为锁
    //生产方法
    public void product() throws InterruptedException {
        //使用代码块,精确加锁,且synchronized会自动释放锁
        synchronized (obj){
            //1.判断什么时候等待
            if(count == capacity){//当实际元素数量达到总容量是,生产阻塞等待
                obj.wait();
            }
            //2.干活
            num++;
            count++;//生产一个数字,元素数量+1
            System.out.println(Thread.currentThread().getName()+
                    "生产了一个数字"+num+",资源池剩余数据个数:"+count);
            //3.干完后后通知唤醒  消费者来消费
            obj.notifyAll();//唤醒其他所有线程,让他们竞争锁
        }
    }
    //消费的方法
    public void consumer() throws InterruptedException {
        synchronized (obj){
            //1.判断什么时候等待
            if(count == 0){//当实际元素数量达到总容量是,生产阻塞等待
                obj.wait();
            }
            //2.干活
            num--;
            count--;//消费一个元素,数量-1
            System.out.println(Thread.currentThread().getName()+
                    "消费了一个数字"+num+",资源池剩余数据个数:"+count);
            //3.干完后后通知唤醒  生产者
            obj.notifyAll();//唤醒其他所有线程,让他们竞争锁
        }
    }
}
public class ProductAndConsumerTest1 {
    public static void main(String[] args) {
        MyCacheResources1 myCacheResources1 = new MyCacheResources1();
        //这里我们直接使用匿名内部类的变相lamda表达式来创建线程
        //生产者
        new Thread(()->{
            for (int i = 1; i <=10 ; i++) {//生产10轮
                try {
                    myCacheResources1.product();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        },"生产者").start();

        //消费者
        new Thread(()->{
            //让生产者先 生产数据
            for (int i = 1; i <=10 ; i++) {//消费10轮,
                try { TimeUnit.SECONDS.sleep(1);}
                catch (InterruptedException e) {e.printStackTrace();}
                try {
                    myCacheResources1.consumer();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者1").start();

        new Thread(()->{
            //让生产者先 生产数据
            for (int i = 1; i <=10 ; i++) {//消费10轮,
                try { TimeUnit.SECONDS.sleep(1);}
                catch (InterruptedException e) {e.printStackTrace();}
                try {
                    myCacheResources1.consumer();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者2").start();

    }
}

Print result:
insert image description here

The second: use Lock and await, signal

package com.fan.blockqueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//定义资源类
class MyCacheResources2{
     int  num = 0;//共享资源:生产和消费数字
    //资源池中实际存储的数据个数
    private int count = 0;
    //资源池中允许存放的资源数目
    private int capacity = 5;
    //创建可重入的非公平锁
    Lock lock = new ReentrantLock();
    //使用两个条件队列condition来实现精确通知
    Condition productCondition = lock.newCondition();
    Condition consumerCondition = lock.newCondition();
    //定义操作资源类的方法:生产方法
    public void product() throws InterruptedException {
        lock.lock();//加锁
        try {
            //1.判断什么时候等待,并防止虚假唤醒
            while(count == capacity){//当达到最大容量,则阻塞等待,生产者阻塞
                productCondition.await();
            }
            //2.干活
            num++;//共享资源+1
            count++;//元素实际个数+1
            System.out.println(Thread.currentThread().getName()+
                    "\t 生产了一个数字:"+num+",现在资源池剩余数据个数:"+count);
            //3.通知。生产者生产完立马通知消费者来消费
            consumerCondition.signal();//消费者条件队列被唤醒
        } finally {
            lock.unlock();//解锁
        }
    }

    //定义操作资源类的方法:生产方法
    public void consumer() throws InterruptedException {
        lock.lock();//加锁,同一把锁
        try {
            //1.判断什么时候等待,并防止虚假唤醒
            while(count == 0){//没数据时,则阻塞等待,消费者阻塞
                consumerCondition.await();
            }
            //2.干活
            //共享资源-1
            count--;//元素实际个数-1
            System.out.println(Thread.currentThread().getName()+
                    "\t 消费了一个数字:"+(num--)+",现在资源池剩余数据个数:"+count);
            //3.通知。消费者 消费完 立马通知生产者来生产
            productCondition.signal();//生产者条件队列被唤醒
        } finally {
            lock.unlock();//解锁
        }
    }
}
public class ProductAndConsumerTest2 {
    public static void main(String[] args) {
        MyCacheResources2 myCacheResources2 = new MyCacheResources2();
        //可以定义多个生产者和消费者,这里分别定义了一个
        new Thread(()->{
            for (int i = 0; i < 10; i++) {//10轮生产
                try {
                    try { TimeUnit.SECONDS.sleep(1);}//让生产者 先  生产
                    catch (InterruptedException e) {e.printStackTrace();}
                    myCacheResources2.product();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"生产者").start();

        new Thread(()->{
            for (int i = 0; i < 10; i++) {//10轮消费
                try {
                    myCacheResources2.consumer();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"消费者").start();
    }
}

Note: Condition can achieve precise notification of which thread is to be woken up. very convenient. And synchronized can't achieve the effect of precise notification .

insert image description here

The third: use blocking queue BlockingQueue

package com.fan.blockqueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

//资源类
class MyResource3{
    private BlockingQueue blockingQueue = new ArrayBlockingQueue(3);
    //线程操作资源类
    //向资源池中添加资源
    public  void add() throws InterruptedException {
        try {
            //put自带锁和通知唤醒方法
            try { TimeUnit.SECONDS.sleep(1);}//模拟生产耗时1秒
            catch (InterruptedException e) {e.printStackTrace();}
            //put方法是自带锁的阻塞唤醒方法,不需要我们写锁,通知和唤醒
            blockingQueue.put(1);
            System.out.println("生产者"+Thread.currentThread().getName()+
                    "生产一件资源,当前资源池有"+blockingQueue.size()+"个资源");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //向资源池中移除资源
    public  void remove(){
        try {
            try { TimeUnit.SECONDS.sleep(1);}//模拟消费耗时1秒
            catch (InterruptedException e) {e.printStackTrace();}
            Object take = blockingQueue.take();//自带锁和通知唤醒方法
            System.out.println("消费者" + Thread.currentThread().getName() +
                    "消耗一件资源," + "当前资源池有" + blockingQueue.size()
                    + "个资源");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
//使用阻塞队列BlockingQueue解决生产者消费者
public class BlockQueueDemo2 {
    public static void main(String[] args) {
        MyResource3 myResource3 = new MyResource3();
        //这里可以通过for循环的次数控制生产者和消费者的比例,来模拟缓存区的缓存剩余情况
        for (int i = 1; i <= 5 ; i++) {//请变换生产者和消费者数量进行测试
            //模拟两个生产者线程
            new Thread(()->{
                while(true){//循环生产
                    try {
                        myResource3.add();//生产数据
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },String.valueOf(i)).start();
        }

        for (int i = 1; i <= 2 ; i++) {//5个消费者
            new Thread(()->{
                while (true){//循环消费
                    myResource3.remove();
                }
            },String.valueOf(i)).start();
        }
    }
}


insert image description here


---------------------
Author: This name is used first
Source: CSDN
Original text: https://blog.csdn.net/weixin_38568503/article/details/123799972
Copyright statement: This article is the author's original article, please attach the blog post link for reprinting!
Content analysis By: CSDN, CNBLOG blog post one-click reprint plug-in

Guess you like

Origin blog.csdn.net/xiaowang_lj/article/details/132024052