Java多线程(5)——生产者消费者模式实践

生产者消费者模式:生产者和消费者要处理同一货物对象,货物对象的属性对生产者和消费者的行为应该产生制约,即有货物才能消费,没有货物就进行生产。通过同步方法,wait()休眠线程,和notify或者notifyall唤醒线程来实现这个逻辑,详情见下面代码:

货物:

package com.way.threads.consumerandproducer;

public class Goods {

    private String name;
    private int num=0;
    private boolean havegoods=false;//用来标记是否有货物的flag

    public synchronized void produce(String name){
        if(havegoods){
            try {
                this.wait();//等待其他进程运行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        this.name=name;
        this.num++;
        System.out.println("Produce "+name+" num: "+num);

        this.havegoods=true;//设置flag
        this.notify();    //唤醒线程

    }

    public synchronized void consume(){
        if(!havegoods){
            try {
                this.wait();//等待其他进程运行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        this.num--;
        System.out.println("Consume "+name+" num: "+num);

        this.havegoods=false;
        this.notify();   //唤醒线程
    }
}

生产者

package com.way.threads.consumerandproducer;

public class Producer implements Runnable{
    //生产者,负责生产货物
    private Goods goods;

    public Producer(Goods goods){
        super();
        this.goods=goods;

    }

    public void run() {
        for(int i=0;i<20;i++){
            if(i%2==0){
               goods.produce("bike"); 
            }else{
                goods.produce("car");
            }
        }
    }

}

消费者

package com.way.threads.consumerandproducer;

public class Consumer implements Runnable{
    //消费者,负责消费货物
    private Goods goods;

    public Consumer(Goods goods){
        super();
        this.goods=goods;
    }

    public void run() {

        for(int i=0;i<20;i++){
            goods.consume();
        }
    }

}

测试

package com.way.threads.consumerandproducer;

public class App {

    public static void main(String[] args){
        Goods goods=new Goods();
        //生产消费同时进行,必须保证有商品的时候才能被消费
        Consumer consumer=new Consumer(goods);
        Producer producer=new Producer(goods);

        new Thread(producer).start();
        new Thread(consumer).start();

    }
}

这个实现是货物只能存在一个,消费一个生产一个,实际也可以可以同时存在多个,那也就变成了“池”,可以对代码进行修改实现。代码如下:

货物:

package com.way.threads.consumerandproducer;

public class Goods {

    private int num=0;

    public synchronized void produce(){
        if(isFull()){
            try {
                this.wait();//等待其他进程运行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if(!isFull()){
            this.num++;
            System.out.println(Thread.currentThread().getName().toString()+" Produce "+" num: "+num);
            try {
                wait(500);//这里是为了让效果明显,不然太快了,看不出线程的切换
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        notifyAll();

    }

    public synchronized void consume(){
        if(isEmpty()){
            try {
                this.wait();//等待其他进程运行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if(!isEmpty()){
            this.num--;
            System.out.println(Thread.currentThread().getName().toString()+" Consume "+" num: "+num);
            try {
                wait(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


        }
        notifyAll();
    }

    public boolean isEmpty(){
        if(num<=0){
            return true;
        }else{
            return false;
        }
    }

    public boolean isFull(){
        if(num>=10){
            return true;
        }else{
            return false;
        }
    }
}

生产者

package com.way.threads.consumerandproducer;

public class Producer implements Runnable{
    //生产者,负责生产货物
    private Goods goods;

    public Producer(Goods goods){
        super();
        this.goods=goods;

    }

    public void run() {
        for(int i=0;i<100;i++){
            goods.produce();
        }
    }

}

消费者

package com.way.threads.consumerandproducer;

public class Consumer implements Runnable{
    //消费者,负责消费货物
    private Goods goods;

    public Consumer(Goods goods){
        super();
        this.goods=goods;
    }

    public void run() {

        for(int i=0;i<100;i++){
            goods.consume();
        }
    }   
}

测试

package com.way.threads.consumerandproducer;

public class App {

    public static void main(String[] args){
        Goods goods=new Goods();
        //生产消费同时进行,必须保证有商品的时候才能被消费
        Consumer consumer=new Consumer(goods);
        Consumer consumer2=new Consumer(goods);
        Consumer consumer3=new Consumer(goods);
        Producer producer=new Producer(goods);
        Producer producer2=new Producer(goods);
        Producer producer3=new Producer(goods);
        new Thread(producer).start();
        new Thread(consumer).start();
        new Thread(consumer2).start();
        new Thread(producer2).start();
        new Thread(consumer3).start();
        new Thread(producer3).start();

    }
}

这里启动了多个生产者和消费者线程,为了更好地展示效果。

部分结果:

Thread-1 Consume  num: 2
Thread-3 Produce  num: 3
Thread-5 Produce  num: 4
Thread-4 Consume  num: 3
Thread-2 Consume  num: 2
Thread-0 Produce  num: 3
Thread-2 Consume  num: 2
Thread-4 Consume  num: 1
Thread-5 Produce  num: 2
Thread-3 Produce  num: 3
Thread-1 Consume  num: 2
Thread-3 Produce  num: 3
Thread-5 Produce  num: 4
Thread-4 Consume  num: 3
Thread-2 Consume  num: 2
Thread-0 Produce  num: 3
Thread-2 Consume  num: 2

可以发现,生产和消费在持续进行着

其他实现方式:

java中实现生产者消费者模式:
最简单的方式是基于Object类的wait()/notify()方法实现:
wait()方法:当缓冲区已满/空时,生产者/消费者线程停止自己的执行,放弃锁,使自己处于等等状态,让其他线程执行。
notify()方法:当生产者/消费者向缓冲区放入/取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。

第二种方式是使用Lock的lock和unlock方法以及Condition相关的await()/signal()方法
使用与wait()/notify()类似
无论是生产者还是消费者,在lock()对“货物”加锁后,根据“容量”条件,使用await()阻塞线程,使用signal()唤醒其他等待线程,最后使用unlock()释放锁。

还可以使用BlockingQueue阻塞队列方法:
BlockingQueue是一个内部实现了同步机制的类,调用其put()和take()方法可以轻松实现生产者消费者模式:
put()方法:类似于我们上面的生产者线程,容量达到最大时,自动阻塞。
take()方法:类似于我们上面的消费者线程,容量为0时,自动阻塞。

阅读更多
版权声明:都是学习笔记,主要给自己看的,有误不负责,欢迎随便用 https://blog.csdn.net/picway/article/details/54412542
文章标签: java 多线程
个人分类: Java 设计模式
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭