关于顾客、厨师、服务员问题的思路及实现

  在CSDN看上看到有楼主提问顾客、厨师、服务员的问题,但描述不是很清楚。我自己梳理了一下,问题应该是这样的:用多线程模拟一个餐馆的运营流程:顾客就餐,服务员为其点菜,将菜单提交给厨师,厨师做好后通知服务员,服务员上菜,顾客用餐,主要是线程同步问题。

  想了一下问题的要点:
  1.因为服务员和厨师都为顾客服务,所以可以把顾客看做被服务员和厨师共享的资源。其实在现实中,顾客点的菜单亦可看做共享的资源 但这里简化了模型 只关心顾客来就餐, 而不关心顾客具体点了什么菜 ,所以无必要考虑菜单这些细节了
  2.服务员不能同时为同一个顾客服务
  3.多个厨师不能同时为做同一个顾客的菜
  4.服务员如何找到等待的顾客并为其上菜
  5.也是那位楼主的疑问,服务员如何同时监听顾客的需求和厨师的通知
  我是用java考虑的 感觉相对会简单一些 对以上的要点的解决思路如下:
  1.定义服务员、顾客、厨师、餐馆4个类,顾客实例数目是不固定的,服务员、厨师则是固定的,4个类别的实例的行为分别用4类线程描述,如:顾客到来、点餐、就餐、离开,等候;服务员服务、上菜;厨师做菜,通知;
  2.将顾客(菜单)看做共享资源 用阻塞队列去同步服务员服务行为
  3.仍将顾客(菜单)看做共享资源 资源由服务员投放 厨师取 资源保持提交顺序 考虑用同步队列去同步
  4.用map记录每个顾客实例作为对应线程的锁监视器,当服务员上菜时从map中找到对应的顾客对象 唤醒其监视器上的等待的线程
  5.可使顾客的需求和厨师的通知指向同一阻塞队列,即服务员监听同一阻塞队列,有个问题是当阻塞队列设置过小,而顾客需求来得太快导致厨师的通知无法投放时,会造成死锁,但是考虑下现实情况,当客源数量超过餐馆容量时总要顾客总要排队,所以我考虑阻塞队列设置对餐馆容量的2倍并实现顾客排队即可。
  梳理思路:
  1.设计服务员、顾客、厨师、餐馆4个类
  2.设计服务员、顾客、厨师、餐馆4类线程
  3.设计两个阻塞队列,其中一个同步顾客线程及厨师线程对服务员线程的通讯,另外一个同步服务员线程与厨师线程通讯,而且顾客线程产生顾客实例作为共享资源
  4.实现顾客排队及顾客等待被唤醒就餐

  <实现如下 仅是个人观点 如有不足和更好的建议 欢迎提出>

package com.java5.learning.concurrent;

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.SynchronousQueue;
/**
* 餐馆
*
@author zengjf
*
*/
public class Restaurent {

private int serverAmount;
private int cookerAmount;
private int seats;//餐馆座位
private BlockingQueue<Client> orderQueue;//服务员任务队列 包括招待顾客和上菜
private BlockingQueue<Client> foodQueue;//厨子任务队列
private ConcurrentMap<Integer, Client> clientWaitingPool;//等待的顾客

public Restaurent(int serverAmount,int cookerAmount,int seats){
this.serverAmount = serverAmount;
this.cookerAmount = cookerAmount;
this.seats = seats;
this.orderQueue = new ArrayBlockingQueue(seats*2);
this.foodQueue = new SynchronousQueue<Client>();
this.clientWaitingPool = new ConcurrentHashMap<Integer, Client>();
}

public static void main(String[] args) throws Exception{
Restaurent restaurent = new Restaurent(3,2,5);
restaurent.doBusiness();
}
/**
* 餐馆运营
*
@throws Exception
*/
public void doBusiness() throws Exception{
serverBehaviour();
cookerBehavior();
Thread.sleep(1000);
clientBehaviour();
}
/**
* 模拟厨子执行任务
*/
private void cookerBehavior() {
ExecutorService cookerExecutor = Executors.newFixedThreadPool(cookerAmount);

for (int i = 0; i < cookerAmount; i++) {
cookerExecutor.execute(new Runnable() {
public void run() {
Employee cooker = EmployeeFactory.getEmployee("Cooker");
Client client;
while(true) {
try {
client = foodQueue.take();
cooker.serve(cooker + "为" + client +"做菜..");
Thread.sleep(new Random().nextInt(6)*1000);
client.setFoodPrepared(true);
orderQueue.put(client);
cooker.serve(cooker + "通知:" + client + "的菜做好了!");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
cookerExecutor.shutdown();
}
/**
* 模拟服务员执行任务
*/
private void serverBehaviour() {
ExecutorService serverExecutor = Executors.newFixedThreadPool(serverAmount);

for (int i = 0; i < serverAmount; i++) {
serverExecutor.execute(new Runnable() {
public void run() {
Employee server = EmployeeFactory.getEmployee("Server");
Client client;
Client clientWaited;
while(true){
try {
client = orderQueue.take();
if (!client.isFoodPrepared()) {
server.serve(server + "正接待" + client + "..");
Thread.sleep(new Random().nextInt(5)*1000);
server.serve(server + "为" + client + "提交菜单..");
foodQueue.put(client);
} else {
server.serve(server + "为" + client + "上菜..");
Thread.sleep(new Random().nextInt(3)*1000);
synchronized (Client.class) {
clientWaited = clientWaitingPool.get(client
.getSeq());
}
synchronized (clientWaited) {
clientWaited.notify();
}
server.serve(server + "为" + client + "上菜完毕..");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
}
serverExecutor.shutdown();
}

/**
* 生产顾客并模拟顾客行为
*/
private void clientBehaviour() {
ExecutorService clientExecutor = Executors.newCachedThreadPool();
for (int i = 0; i <20; i++) {
clientExecutor.execute(new Runnable() {
public void run() {
Client client;
try {
Thread.sleep(new Random().nextInt(10)*1000);

synchronized (Client.class) {
client = ClientFactory.newClient();
client.come();
while(clientWaitingPool.size() >= seats){
client.waiting();
Client.class.wait();
client.enter();
}

orderQueue.put(client);
clientWaitingPool.put(client.getSeq(),client);
}

synchronized (client) {
client.wait();
}

client.eat();
Thread.sleep(new Random().nextInt(10)*1000);
client.leave();

synchronized (Client.class) {
clientWaitingPool.remove(client.getSeq());
Client.class.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
clientExecutor.shutdown();
}


}
/**
* 工厂:产生顾客
*
@author zengjf
*
*/
class ClientFactory{
public static int order = 0;

public static Client newClient(){
return new Client(++order);
}
}
/**
* 顾客类
*
@author zengjf
*
*/
class Client{
int seq;
boolean preparedTag;
public Client(int seq){
this.seq = seq;
this.preparedTag = false;
}
@Override
public String toString() {
return seq+"号客人";
}
public int getSeq(){
return seq;
}
public boolean isFoodPrepared(){
return preparedTag;
}
public void setFoodPrepared(boolean tag){
preparedTag = tag;
}

public void come(){
System.out.println(this + "到来..");
}
public void eat(){
System.out.println(this + "用餐..");
}
public void leave(){
System.out.println(this + "离开..");
}
public void waiting(){
System.out.println("客满," + this + "在等候..");
}
public void enter(){
System.out.println(this + "进入..");
}
}
/**
* 工厂:产生雇员
*
@author zengjf
*
*/
class EmployeeFactory{
private static char num = 'A';
public static String genJobNumber(){
return (char)(num++) + "";
}

public static Employee getEmployee(String type){
if(type.equals("Server")){
return new Server(genJobNumber());
}else{
return new Cooker(genJobNumber());
}
}
}

interface Employee{
String getJobNumber();
void serve(String content);
}
/**
* 服务员
*
@author zengjf
*
*/
class Server implements Employee{
String jobNumber;
public Server(String jobNumber){
this.jobNumber = jobNumber;
}
@Override
public String toString() {
return "服务员["+jobNumber+"]";
}

public String getJobNumber(){
return jobNumber;
}
public void serve(String content){
System.out.println(content);
}
}
/**
* 厨子
*
@author zengjf
*
*/
class Cooker implements Employee{
String jobNumber;
public Cooker(String jobNumber){
this.jobNumber = jobNumber;
}
@Override
public String toString() {
return "厨子["+jobNumber+"]";
}

public String getJobNumber(){
return jobNumber;
}

public void serve(String content){
System.out.println(content);
}

}

有一问题,后来看文档解决了:java5的ConcurrentHashMap的更新操作是线程安全的,但get操作是线程不安全的,亦即多线程操作ConcurrentHashMap的话,get会引起NullPoint异常或数据不同步问题,所以要自行加锁

posted @ 2012-03-15 00:24  Goodspeed85  阅读(653)  评论(0编辑  收藏  举报