Java并发编程系列之十三 生产者-消费者模式

                       

生产者-消费者模式指的是:生产者和消费者在同一个时间段共用同一段空间,在这段时间内,生产者负责往存储空间生产数据,而消费者则负责消费数据。实际上存在很多类似的场景:消息中间件就可以看做这种模式的应用,客户端请求负责把请求消息发送给消息中间件,然后由服务器负责从消息中间件获取请求并进行响应;还有网络编程中Socket也可以看做是生产者消费者模式的应用,etc。

为了简化问题的研究,现在仅仅考虑一种简单的场景:生产者负责生产“authorA–>titleA”和“authorB–>titleB”两种数据(这两种数据是一种代表author和title可以认为是生产的两种组件,并无实际意义),消费者则负责从中取数据。根据前面对线程安全的研究,该场景可能会出现取到的数据不完整或者重复取数据的情况。出现数据完整表现在:生产者生产完author后,还没有来得及生产title便被消费者取走了,这种情况需要使用线程同步机制实现;取到重复的数据表现在:消费者取到完整的数据后,没有及时通知生产者继续生产,导致第二次取到的数据仍然是上次取到的数据,显然这种情况需要使用等待/通知机制实现。

首先我们来定义需要生产的组件:Content。Content包括了两个组件信息:author和title,并提供了消费者和生产者执行的get和set方法

package com.rhwayfun.concurrency;import java.util.concurrent.TimeUnit;/** * Created by rhwayfun on 16-4-4. */public class Content {    //标题    private String title;    //作者    private String author;    //是否开始生产的标志,默认开始生产    private boolean produce = true;    /**     * 消费者执行的方法     * @param author     * @param title     * @throws InterruptedException     */    public synchronized void set(String author, String title) throws InterruptedException {        //如果没有开始生产就阻塞等待        while (!produce) {            super.wait();        }        //设置作者        this.setAuthor(author);        //休眠1秒        TimeUnit.SECONDS.sleep(1);        //设置标题        this.setTitle(title);        System.out.println("[生产者]:" + this.getAuthor() + " --> " + this.getTitle());        //设置标志位为false。表示可以取数据了        produce = false;        //唤醒正在等待的线程        super.notify();    }    /**     * 消费者执行的方法     * @throws InterruptedException     */    public synchronized void get() throws InterruptedException {        //如果已经开始生产了就阻塞等待        while (produce){            super.wait();        }        System.out.println("[消费者]:" + this.getAuthor() + " --> " + this.getTitle());        //让生产者继续生产        produce = true;        super.notify();    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    public String getAuthor() {        return author;    }    public void setAuthor(String author) {        this.author = author;    }}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

然后,我们定义了生产者,把生产者执行的生产任务放在一个线程中单独执行:

package com.rhwayfun.concurrency;/** * Created by rhwayfun on 16-4-4. */public class Producer implements Runnable {    private Content content;    public Producer(Content content) {        this.content = content;    }    public void run() {        boolean flag = true;        for (int i = 0; i < 6; i++){            if (flag){                try {                    content.set("authorA","titleA");                } catch (InterruptedException e) {                    e.printStackTrace();                }                flag = false;            }else {                try {                    content.set("authorB","titleB");                } catch (InterruptedException e) {                    e.printStackTrace();                }                flag = true;            }        }    }}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

接着,我们定义消费者,消费者的消费任务也放在线程中单独执行:

package com.rhwayfun.concurrency;/** * Created by rhwayfun on 16-4-4. */public class Consumer implements Runnable {    private Content content;    public Consumer(Content content) {        this.content = content;    }    public void run() {        for (int i = 0; i < 6; i++){            try {                content.get();            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

最后,就是我们的测试代码了:

package com.rhwayfun.concurrency;/** * Created by rhwayfun on 16-4-4. */public class ProducerConsumerModeTest {    public static void main(String[] args){        Content content = new Content();        Thread producer = new Thread(new Producer(content),"Producer");        Thread consumer = new Thread(new Consumer(content),"Consumer");        producer.start();        consumer.start();    }}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

运行上面的测试代码,得到的结果如下:

这里写图片描述

从运行结果可以看出,只有当生产者生产了数据后消费者才会从中取数据,这样取到的数据就不会重复。由一点要注意的是:消费者线程循环的次数必须和生产者线程一致,不然就会出现消费者一直等待生产者生产数据的情况,这点从代码中就可以看出,因为如果生产者没有继续生产的话消费者调用wait方法便会阻塞等待直到有数据。

           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

猜你喜欢

转载自blog.csdn.net/qq_43667968/article/details/86573190