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