生产者-消费者
1. 问题建模
在餐厅中,厨师是肉的生产者,服务员是肉的消费者。
1.1详细设计
Restaurant
类中有未初始化的Meal
类和进行工作的WaitPerson
对象、Chef
对象:前者负责消费Meal
,后者负责生产,而且两者需要在构造参数中定义其服务的餐厅。
1.2代码实现
package concurrency;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @author 杜艮魁
* @date 2018/1/31
*/
//Meal是Restaurant中未初始化的一个实例,并被其另外两个对象WaitPerson和Chef不断置null和实例化;
class Meal{
private final int orderNum;
public Meal(int orderNum) {
this.orderNum = orderNum;
}
@Override
public String toString() {
return "Meal "+orderNum;
}
}
//应当注意WaitPerson是Restaurant中的一个对象,对象创建方式
// WaitPerson waitPerson=new WaitPerson(this) 调用其构造参数如下,以此来使WaitPerson
//作为Restaurant的变量的同时能够操作Restaurant中的变量:即Meal引用状态和唤醒Chef线程
class WaitPerson implements Runnable{
private Restaurant restaurant;
public WaitPerson(Restaurant restaurant) {
this.restaurant = restaurant;
}
@Override
public void run() {
try{
while(!Thread.interrupted()){
synchronized (this){
//对条件的判定放在锁里边,防止产生竞态条件
while(restaurant.meal==null)//
//加锁对象是this-WaitPerson对象:当厨房没有肉时,服务员等待;
wait();
}
System.out.println("waiter got "+restaurant.meal);
//Meal不为null时Chef类中用this加锁的代码块被挂起。而以下代码将Meal置为null,因此需要唤醒Chef对象,修改meal引用;
synchronized (restaurant.chef){
//服务员已经拿到肉,此时将肉实例设置为空,并唤醒等待上行锁的线程
restaurant.meal=null;
restaurant.chef.notifyAll();//fixme notifyAll()相当于this.notifyAll(),唤醒的是等待当前对象监视器的所有线程而非等待Restaurant对象中Chef对象监视器的线程;
}
}
}catch (InterruptedException e){
System.out.println("waiter interrupted");
}
}
}
//含义同WaitPerson
class Chef implements Runnable{
private int count=0;
Restaurant restaurant;
public Chef(Restaurant restaurant) {
this.restaurant = restaurant;
}
@Override
public void run() {
try{
while(!Thread.interrupted()){
synchronized (this){
while(restaurant.meal!=null)
wait();
}
if(++count==10){
System.out.println("out of food,closing");
restaurant.exec.shutdownNow();//注意停止的方法仍然是调用Restaurant中的方法
}
System.out.println("order up!");//要上菜啦!!
synchronized (restaurant.waitPerson){
restaurant.meal=new Meal(count);
//对应tag1处waitPerson发现"Meal"实例不为空时,在synchronized(this-waitPerson)代码块中调用wait——这句代码唤醒wait()的等待
restaurant.waitPerson.notifyAll();
}
TimeUnit.MILLISECONDS.sleep(100);
}
}catch (InterruptedException e){
System.out.println("chef interrupted");
}
}
}
public class Restaurant {
Meal meal;
WaitPerson waitPerson=new WaitPerson(this);
Chef chef=new Chef(this);
ExecutorService exec= Executors.newCachedThreadPool();
public Restaurant() {//创建对象时开始执行这两个线程
exec.execute(chef);
exec.execute(waitPerson);
}
public static void main(String[] args) {
new Restaurant();
}
}