Oracle Java官网关于生产者消费者的示例解析
思考1:如果邮箱的empty是否为空属性初始值为false,会出现什么问题?
思考2:为何要加随机睡眠的处理?如果不加,执行结果会有何不同?
一、[Why]官网为何要提供生产者消费者这么个示例?
通过模拟现实生活中一边生产一遍消费的场景,来说明:通过给方法加锁、轮询条件是否满足、以及适时地协调线程间通信(wait/nofity)的方式,可以确保多线程间能够以一种正确且高效的方式访问共享资源。
二、[What]相关类描述
1.邮箱(MailBox)
1). 对应官网的Drop类,为了便于理解,我这边把名称改成了MailBox,实际代码内容一致;
2). 邮箱有两个属性:一个是message消息属性【初始值为null】,一个是empty是否为空的属性【初始值为true】;
3). 邮箱有两个行为:一个是put存放消息行为【只有当邮箱为空时才允许put存放消息】,一个是take取出消息行为【只有当邮箱非空时才允许take取出消息】
4). 邮箱是供生产者和消费者操作的对象,生产者往邮箱里put存放消息,消费者从邮箱take取出消息;
5). 代码如下:
public class MailBox {
private String message;
private boolean empty = true;
public synchronized String take() {
while(empty) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
empty = true;
notifyAll();
return message;
}
public synchronized void put(String message) {
while(!empty) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
empty = false;
this.message = message;
notifyAll();
}
}
2.生产者类(Producer)
1). 生产者实现了Runnable接口,以便用于被线程用于执行生产任务;
2). 生产者有一个MailBox邮箱的引用,在构造方法里被初始化;
3). 生产者只负责一件事:通过调用邮箱的put方法生产(即存放消息);
4). 代码如下:
public class Producer implements Runnable{
private MailBox mailBox;
Producer(MailBox mailBox){
this.mailBox = mailBox;
}
@Override
public void run() {
String importantInfo[] = {
"Mares eat oats",
"Does eat oats",
"Little Lambs eat ivy",
"A kid will eat ivy too"
};
Random random = new Random();
for(int i = 0; i < importantInfo.length; i++ ) {
mailBox.put(importantInfo[i]);
try {
Thread.sleep(random.nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
mailBox.put("DONE");
}
}
3.消费者(Consumer)
1). 消费者实现了Runnable接口,以便用于被线程用于执行消费任务;
2). 消费者有一个MailBox邮箱的引用,在构造方法里被初始化;
3). 消费者只负责一件事:通过调用邮箱的take方法消费(即取出消息);
4). 代码如下:
public class Consumer implements Runnable{
private MailBox mailBox;
Consumer(MailBox mailBox){
this.mailBox = mailBox;
}
@Override
public void run() {
Random random = new Random();
for(String message = mailBox.take(); !message.equals("DONE");
message = mailBox.take()) {
System.out.format("MESSAGE RECEIVED: %s%n", message);
try {
Thread.sleep(random.nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.format("%s%n", "DONE");
}
}
4.测试执行类(ProducerConsumerTest)
1). 测试执行类负责创建邮箱;
2). 测试执行类负责启动生产者和消费者线程,模拟消息的生产和消费过程;
3). 代码如下:
public class ProducerConsumerTest {
public static void main(String[] args) {
MailBox mailBox = new MailBox();
new Thread(new Producer(mailBox)).start();
new Thread(new Consumer(mailBox)).start();
}
}
三、官网原文链接
https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html