Java-生产者与消费者模型

线程同步与互斥的经典问题就是生产者与消费者的问题,首先介绍一下这个问题:

        1:生产者将生产的产品放到某个位置存储起来(仓库),而消费者从仓库消费产品。

        2:仓库满了就停止生产,生产者等待消费者来消费产品,当仓库不满的时候生产者继续生产。

        3:如果仓库是空的,消费者就等待生产者生产产品,然后再消费。

实现此问题所用的仓库我使用队列来实现,队列是先进先出的操作,满足实际情况。

        实现此模型,我们需要有一个大致的框架,主要分为四个板块。

1:产品类

        这是最简单的一个类:主要描述生产产品的信息。为了区别出每个产品,产品类中有一个私有属性box_id。在创建一个产品的时候给它一个有别于其他产品的id。现在看一下实现代码。

class box{//产品类
    private int box_id;
    box(int box_id){
        this.box_id = box_id;
    }

    public int getBox_id() {
        return box_id;
    }

    @Override
    public String toString() {
        return "产品:"+this.box_id;
    }
}

        我同时覆写了toString()方法,为了在输出的时候可以更加方便的区分出每一个产品。

2:仓库类(队列实现)

        我使用LinkedList类实现Queue接口实现了一个队列,用来存放产品类对象,这个队列作为仓库类的私有属性定义,仓库类中有一个生产(存)的方法和一个消费(存)的方法,在生产的方法中定义了仓库的存储上限。先看一下实现的代码。

class MyQueue{//存放产品的仓库类
    private Queue<box> queue = new LinkedList<>();//产品存放队列

    public synchronized void push(box box) throws InterruptedException {//产品放入队列
        int COUNT = 10;//限定了队列的最大长度
        if(queue.size() == COUNT){//如果队列已满
            this.wait();//线程进入等待状态
        }
        //队列不满就为空
        queue.offer(box);//产品入队列
        if(queue.size() != COUNT) {//如果产品入队列之后队列还没有满
            this.notifyAll();//唤醒所有等待线程开始生产
        }
    }

    public synchronized box Pop() throws InterruptedException {//把产品取出
        if(queue.size() == 0){
            this.wait();
        }
        box ret = queue.poll();//消费掉产品:poll()返回并删除队首元素
        if(queue.size() != 0) {//如果消费之后队列中还有产品
            this.notifyAll();//唤醒所有的消费线程开始消费产品
        }
        return ret;
    }

}

        这个类中定义了两个重要的方法,push()方法把产品放入队列,如果队列满了,就唤醒消费线程去消费产品。反之,pop()方法如果发现队列为空,那么就唤醒生产线程去生产产品。我这里使用的是notifyAll(),就是说唤醒所有线程,就可以实现一边生产一边消费的情况了。

        这里还有几点需要注意:例如在把产品入队列之后必须判断此时的队列是否已经满了,满了的话就不需要唤醒生产线程去生产了。对应的,产品出队列之后如果没有产品就不用唤醒线程去消费了。

3:生产者类

        生产者类中含有仓库类的对象,生产的产品数。仓库类对象通过构造注入,生产类继承Runnable接口,可以实现更好的资源共享,我先看一下代码。

class Consumer implements Runnable{//消费者类
    private MyQueue myQueue;
    private int BUY = 20;//规定消费者最多只能买20个

    Consumer(MyQueue myQueue){//构造注入
        this.myQueue = myQueue;
    }

    @Override
    public void run() {

        while (BUY > 0) {
            try {
                Thread.sleep(1500   );//每隔1秒消费掉一件产品
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this) {//加锁原理与生产类相同
                if (BUY <= 0) {
                    return;
                }
                box box = null;//消费者购买了一个玩具
                try {
                    box = myQueue.Pop();//消费掉了一件产品
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "消费了一个产品 id为 " + box.getBox_id());
                BUY--;
            }
        }

    }
}

        接下来我列出所有的代码

package dxy.java;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.Queue;


public class Main {

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {

        Class<?> myQueueClass = MyQueue.class;
        Class<?> consumersClass = Consumer.class;
        Class<?> producersClass = Producter.class;
        MyQueue myQueue =(MyQueue)myQueueClass.newInstance();//反射创建产品存储类对象(仓库)

        Constructor constructor = consumersClass.getDeclaredConstructor(MyQueue.class);
        Consumer consumer = (Consumer)constructor.newInstance(myQueue);//反射创建消费者类对象

        Constructor constructor1 = producersClass.getDeclaredConstructor(MyQueue.class);
        Producter producter = (Producter)constructor1.newInstance(myQueue);//反射创建生产者类对象

        new Thread(consumer,"消费者A").start();//起三个消费者线程
        new Thread(consumer,"消费者B").start();
        new Thread(consumer,"消费者C").start();

        new Thread(producter,"生产者B").start();//起三个生产者类对象
        new Thread(producter,"生产者A").start();
        new Thread(producter,"生产者C").start();
    }
}



class box{//产品类
    private int box_id;
    box(int box_id){
        this.box_id = box_id;
    }

    public int getBox_id() {
        return box_id;
    }

    @Override
    public String toString() {
        return "产品:"+this.box_id;
    }
}

class MyQueue{//存放产品的仓库类
    private Queue<box> queue = new LinkedList<>();//产品存放队列

    public synchronized void push(box box) throws InterruptedException {//产品放入队列
        int COUNT = 10;//限定了队列的最大长度
        if(queue.size() == COUNT){//如果队列已满
            this.wait();//线程进入等待状态
        }
        //队列不满就为空
        queue.offer(box);//产品入队列
        if(queue.size() != COUNT) {//如果产品入队列之后队列还没有满
            this.notifyAll();//唤醒所有等待线程开始生产
        }
    }

    public synchronized box Pop() throws InterruptedException {//把产品取出
        if(queue.size() == 0){
            this.wait();
        }
        box ret = queue.poll();//消费掉产品:poll()返回并删除队首元素
        if(queue.size() != 0) {//如果消费之后队列中还有产品
            this.notifyAll();//唤醒所有的消费线程开始消费产品
        }
        return ret;
    }

}


class Producter implements Runnable {//生产者类
    private MyQueue myQueue;//存放产品的队列
    private int Product = 20;//规定生产者最多只能生产20个产品
    private int id = 1;//生产的产品编号


    Producter(MyQueue myQueue){//构造注入
        this.myQueue = myQueue;
    }

    @Override
    public void run() {
        while (Product > 0) {
            try {
                Thread.sleep(5);//每隔0.1秒生产一个产品
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this) {//每次只能有一个线程在生产产品
                if (Product <= 0) {
                    return;
                }//二重保障,因为生产过程在循环内
                box New_Box = new box(id);//生产一个产品
                try {
                    myQueue.push(New_Box);//产品入库
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 生产了一个产品,id为 " + id++);
                Product--;
            }
        }

    }
}

class Consumer implements Runnable{//消费者类
    private MyQueue myQueue;
    private int BUY = 20;//规定消费者最多只能买20个

    Consumer(MyQueue myQueue){//构造注入
        this.myQueue = myQueue;
    }

    @Override
    public void run() {

        while (BUY > 0) {
            try {
                Thread.sleep(1500   );//每隔1秒消费掉一件产品
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this) {//加锁原理与生产类相同
                if (BUY <= 0) {
                    return;
                }
                box box = null;//消费者购买了一个玩具
                try {
                    box = myQueue.Pop();//消费掉了一件产品
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "消费了一个产品 id为 " + box.getBox_id());
                BUY--;
            }
        }

    }
}



        以下是输出结果:

生产者B 生产了一个产品,id为 1
生产者C 生产了一个产品,id为 2
生产者A 生产了一个产品,id为 3
生产者B 生产了一个产品,id为 4
生产者C 生产了一个产品,id为 5
生产者A 生产了一个产品,id为 6
生产者B 生产了一个产品,id为 7
生产者C 生产了一个产品,id为 8
生产者A 生产了一个产品,id为 9
生产者B 生产了一个产品,id为 10
消费者C消费了一个产品 id为 1
生产者A 生产了一个产品,id为 11
消费者A消费了一个产品 id为 2
生产者C 生产了一个产品,id为 12
消费者B消费了一个产品 id为 3
生产者B 生产了一个产品,id为 13
消费者C消费了一个产品 id为 4
消费者B消费了一个产品 id为 5
生产者A 生产了一个产品,id为 14
消费者A消费了一个产品 id为 6
生产者B 生产了一个产品,id为 15
生产者C 生产了一个产品,id为 16
消费者A消费了一个产品 id为 7
消费者B消费了一个产品 id为 8
生产者B 生产了一个产品,id为 17
消费者C消费了一个产品 id为 9
生产者A 生产了一个产品,id为 18
生产者C 生产了一个产品,id为 19
消费者B消费了一个产品 id为 10
生产者B 生产了一个产品,id为 20
消费者A消费了一个产品 id为 11
消费者C消费了一个产品 id为 12
消费者B消费了一个产品 id为 13
消费者A消费了一个产品 id为 14
消费者C消费了一个产品 id为 15
消费者B消费了一个产品 id为 16
消费者A消费了一个产品 id为 17
消费者C消费了一个产品 id为 18
消费者B消费了一个产品 id为 19
消费者A消费了一个产品 id为 20


Process finished with exit code 0

        我们可以发现:生产者先生产了十个产品,这时候队列满了,生产者不再生产。然后消费者一边消费,生产者就一边生产,生产者生产了20个产品之后不再生产,消费者最终把产品消费完。


        这就是我总结的关于生产者与消费者模型,在线程唤醒那里有一点小问题,不过不影响整体。希望大家能够给我一些指点。

猜你喜欢

转载自blog.csdn.net/qq_38449518/article/details/80428730