Oracle Java官网关于生产者消费者的示例解析

5 篇文章 0 订阅
2 篇文章 0 订阅

思考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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dylanioucn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值