Thinking in Java---多執行緒模擬:銀行出納員模擬+飯店模擬+汽車裝配工廠模擬

weixin_34259232發表於2017-07-11

多執行緒一個非常有意思的作用就是用於模擬,這篇部落格就會結合幾個模擬例項來綜合運用一下前面所學的多執行緒併發知識。

一.銀行出納員模擬
問題描寫敘述:銀行會有非常多來辦業務的顧客,他們會排隊等待服務;對於銀行方面他們派出出納員來服務顧客,假設排隊的顧客數量過多,銀行就會新增
出納員的數量,假設顧客的數目過少。則降低出納員的數目;總之要保持一個平衡。

模擬思路:封裝Customer類來表示顧客,每一個顧客物件都會有一個須要服務的時間;使用有限容量的堵塞佇列CustomerLine來模擬顧客的排隊佇列;封裝
CustomerGenerator類來產生顧客,然後將產生的顧客加入到CustomerLine中去。封裝Teller類來表示銀行的出納員,Teller會從CustomerLine中取出。
Customer來進行服務。封裝TellerManage來管理全部的Teller及依據顧客/出納員的比例來調整服務顧客的Teller數量。在這裡我們通過堵塞佇列CustomerLine實現了Teller執行緒和CustomerGenerator執行緒之間的通訊。

詳細的實現程式碼例如以下:


package lkl;

import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 多執行緒模擬銀行出納員問題
 * */

//模擬顧客類,全然僅僅是一個可讀類。不須要同步
class Customer{
    //該顧客所需服務時間
    private final int serviceTime;

    public Customer(final int serviceTime){
        this.serviceTime = serviceTime;
    }
    public int getServiceTime(){
        return serviceTime;
    }
    public String toString(){
        return "["+serviceTime+"]";
    }
}

//模擬顧客排隊的佇列,繼承了堵塞佇列
//是一個多執行緒共享物件,這個佇列繼承的是ArrayBlocingQueue
//是一個有最大長度的佇列
class CustomerLine extends ArrayBlockingQueue<Customer>{

    //指定同意佇列的最大長度
    public CustomerLine(int maxSize){
        super(maxSize);
    }

    //重寫toString()方法。用來進行顯示當前排隊中的顧客
    public String toString(){
        if(this.size()==0)
            return "[Empty]";
        StringBuilder result = new StringBuilder();
        for(Customer customer :this){
            result.append(customer);
        }
        return result.toString();
    }
}

//顧客生產類
//間隔隨機然後向佇列中加入一位顧客的執行緒
class CustomerGenerator implements Runnable{
    private CustomerLine customerLine; //堵塞佇列
    private static Random rand = new Random(47);
    public CustomerGenerator(CustomerLine customerLine){
        this.customerLine = customerLine;
    }

    public void run(){
        try{
            while(!Thread.interrupted()){
                //執行緒睡眠隨機時間以後,產生一個顧客物件,加入到佇列中
                TimeUnit.MILLISECONDS.sleep(rand.nextInt(300));
                //加入一個服務時間隨機的顧客
                customerLine.add(new Customer(rand.nextInt(1000)));
            }
        }catch(InterruptedException ex){
            System.out.println(this+" 通過中斷異常退出");
        }
        System.out.println(this+" terminating");
    }
}

//出納員類。負責對佇列中的顧客進行服務
//注意其有兩種狀態:服務顧客或做一些其他的事情
class Teller implements Runnable,Comparable<Teller>{
    private static int counter = 0;
    private final int id = counter++;

    //該Teller服務的顧客佇列
    private CustomerLine customerLine;
    private int customerServed = 0;//已服務的顧客數

    //標誌眼下是被分配到服務CustomerLine還是做一些其他事
    //預設是分配給customerLine
    private boolean servingCustomerLine=true; 
    public Teller(CustomerLine cl){
        this.customerLine = cl;
    }

    //正常情況下會從CustomerLine中取出一個Customer進行服務
    //假設被分配到做其他事,則會被掛起
    public void run(){
        try{
            while(!Thread.interrupted()){
                Customer customer = customerLine.take();

                //睡眠一段時間模擬服務Customer
                TimeUnit.MILLISECONDS.sleep(customer.getServiceTime());
                synchronized(this){
                    while(!servingCustomerLine){//被分配做其他事情
                        wait();
                    }
                }
            }
        }catch(InterruptedException ex){
            System.out.println(this+"通過中斷異常退出");
        }
        System.out.println(this+"Terminating");
    }

    //呼叫這種方法意味著該Teller物件被分配去做其他事情
    public synchronized void doSomethingElse(){
        customerServed = 0;
        servingCustomerLine=false; //設定標誌。是當前服務執行緒掛起
    }

    //被分配到服務到customerLine
    public synchronized void serveCustomerLine(){
        servingCustomerLine = true;
        notifyAll();//通知掛起執行緒
    }

    public String toString(){
        return "Teller "+id+" ";
    }
    public String shortString(){
        return "T "+id;
    }

    //按以服務顧客數確定Teller的優先順序,給優先佇列使用
    @Override
    public synchronized int compareTo(Teller other){
        return customerServed < other.customerServed ?

-1: (customerServed==other.customerServed ?

0 :1); } } //服務管理和排程Teller的類 //這個TellerManager類是各種活動的中心,它跟蹤全部的出納員以及等待服務的顧客 //從adjustTellerNumber()中能夠看到,它會依據實際情況調整服務CustomerLine的 //Teller數量,以期達到最優出納員的數目。 class TellerManager implements Runnable{ private ExecutorService exec; //負責啟動Teller執行緒 private CustomerLine customerLine; //按服務顧客數由少到多優先的優先佇列,用來進行排程 //每次都取出服務顧客數最少的出納員來進行服務,以保證公平性。 private PriorityQueue<Teller> workingTellers = new PriorityQueue<>(); //正在做其他事情的Teller佇列 private Queue<Teller> tellersDoingOtherThings = new LinkedList<Teller>(); private int adjustmentPeriod; //排程時間 private static Random rand = new Random(); public TellerManager(ExecutorService exec,CustomerLine customerLine,int adjustmentPeriod){ this.exec =exec; this.customerLine = customerLine; this.adjustmentPeriod = adjustmentPeriod; //在構造器中先分配一個Teller進行服務 Teller teller = new Teller(customerLine); exec.execute(teller); workingTellers.add(teller); } //通過當前customerLine中的顧客數以及正在工作的Teller //人數的比例關係,來確定是否要加/減Teller的數目 public void adjustTellerNumber(){ //假設customerLine佇列過長,則新增服務的Teller if(customerLine.size()/workingTellers.size()>2){ //假設在做其他事的Teller則從中抽調出人來,否則又一次分配一個Teller if(tellersDoingOtherThings.size()>0){ Teller teller = tellersDoingOtherThings.remove(); teller.serveCustomerLine(); workingTellers.add(teller); return; } //又一次分配一個Teller Teller teller = new Teller(customerLine); exec.execute(teller); workingTellers.add(teller); return; } //當前Tellers過多時,抽調一些去做其他工作 if(workingTellers.size()>1&&customerLine.size()/workingTellers.size()<2){ reassignOneTeller(); //假設這裡僅僅有沒有customer須要服務。則僅僅需留下一個Teller if(customerLine.size()==0){ while(workingTellers.size()>1){ reassignOneTeller(); } } } } private void reassignOneTeller() { //從工作佇列中取出一個Teller來 Teller teller = workingTellers.poll(); teller.doSomethingElse();//讓他去做其他工作 tellersDoingOtherThings.offer(teller); } public void run(){ try{ while(!Thread.interrupted()){ TimeUnit.MILLISECONDS.sleep(adjustmentPeriod); //按當前情況進行動態調整 adjustTellerNumber(); //列印當前的customerLine和workingTeller的情況 //從結果能夠看到隨著customerLine大小的變化,workingTeller //的人數也是不斷變化的。

System.out.print(customerLine+"{"); for(Teller teller: workingTellers){ System.out.print(teller.shortString()+" "); } System.out.println("}"); } }catch(InterruptedException ex){ System.out.println(this+"通過中斷異常退出"); } System.out.println(this+"terminating"); } public String toString(){ return "TellerManager"; } } public class BankTellerSimulation { static final int SIZE = 50;//顧客佇列的最大長度 static final int PERIOD = 1000;//調整時間間隔 public static void main(String[] args) throws Exception{ ExecutorService exec = Executors.newCachedThreadPool(); CustomerLine customerLine = new CustomerLine(SIZE); exec.execute(new CustomerGenerator(customerLine)); exec.execute(new TellerManager(exec,customerLine,PERIOD)); System.out.println("Press 'Enter' to exit"); System.in.read(); exec.shutdownNow(); } }

二.飯店模擬
問題描寫敘述:模擬飯店的場景:飯店中有顧客到來以後就會派一個侍者進行服務。然後侍者記錄顧客所點的食物以後就提交訂單到飯店,然後飯店的廚師取的訂單
以後就做好食物然後再由相應的侍者交給顧客。

模擬思路:封裝Oder類表示使用者的訂單,訂單中包括了點餐的顧客,相應的侍者和顧客所點的食物;封裝Plate類表示裝有廚師做好訂單上食物的盤子;封裝Customer類
表示顧客,每一個顧客會隨機選擇一種食物然後由服務該顧客的侍者提交訂單給飯店,當食物做好以後,顧客吃掉完畢消費過程。封裝WaitPerson類表示侍者,侍者一方面幫助服務的顧客提交訂單,還有一方面將飯店廚師做好的食物交給相應的顧客;封裝Chef表示飯店的廚師,廚師從飯店中取得侍者提交的訂單。然後做完當中的食物,然後將相應的Plate提交給該訂單相應的WaitPerson。封裝Restaurant類表示飯店,飯店中有廚師佇列,侍者佇列,訂單佇列,飯店程式中還會每隔一段時間生成一個顧客。


值得注意的是這裡事實上牽涉到了多個執行緒之間協調,可是這些並非通過直接的執行緒之間的通訊來實現的而是通過堵塞佇列來實現的。比方說顧客點了食物以後,侍者會提交一份訂單,可是這份訂單不是給廚師的,而是提交給飯店的訂單堵塞佇列。然後廚師從這個訂單佇列中取出訂單製作好食物以後並不須要直接通知侍者,而是會提交給侍者的堵塞佇列,然後侍者再從它的堵塞佇列中取出食物來提交給顧客的堵塞佇列,然後顧客在合適的時間從其佇列中取出食物來食用。從上面的過程中能夠看到使用佇列極大地降低了執行緒間通訊的複雜度:任務之間沒有直接的相互干涉,而是經由佇列來相互傳送物件。接收任務將處理物件。將其當成一個訊息來對待。而不是向它傳送訊息。

詳細實現程式碼例如以下:


package lkl;

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;

class Course{
    private static Random rand = new Random();
    public static String[]  food={"food1","food2","food3","food4"};
    public static String randomSelection(){
        return food[rand.nextInt(food.length)];
    }
}

//封裝的訂單類
class Order{
    private static int counter=0;
    private final int id = counter++; //訂單唯一的編號
    private final Customer customer; //訂單相應的顧客
    private final WaitPerson waitPerson; //負責該訂單的服務員
    private final String food; //訂單相應的食物

    public Order(Customer cust,WaitPerson wait,String food){
        this.customer = cust;
        this.waitPerson = wait;
        this.food = food;
    }
    //返回訂單中的食物
    public String item(){
        return food;
    }

    public Customer getCustomer(){
        return customer;
    }

    public WaitPerson getWaitPerson(){
        return waitPerson;
    }

    public String toString(){
        return "Order: "+id+"item: "+food+" for: "+customer+" served by: "+waitPerson;
    }
}

//裝好食物的碟子類
class Plate{
    private final Order order; //該碟子相應的訂單
    private  final String food; //該碟子盛放的食物
    public Plate(Order order , String food){
        this.order = order;
        this.food = food;
    }

    public Order getOrder(){
        return order;
    }
    public String getFood(){
        return food;
    }

    public String toString(){
        return food;
    }
}

//顧客類
class Customer implements Runnable{
    private static int counter = 0; 
    private final int id = counter++; //顧客id
    private final WaitPerson waitPerson ;//服務該顧客的侍者

    //表示顧客面前的盤子,在我們的模擬中顧客僅僅會消費一種食物,所以我們使用了
    //容量為1的堵塞佇列SynchronousQueue來表示其前面的盤子,這個佇列每一個put()操作
    //後面都必須跟一個take()操作,否則就會堵塞。
    private SynchronousQueue<Plate> placeSetting = new SynchronousQueue<Plate>();

    public Customer(WaitPerson wait){
        this.waitPerson = wait;
    }
    //將製作完畢的食物提交給顧客,假設前面已經put()過而且
    //使用者還沒有take()則會堵塞
    public void deliver(Plate p) throws InterruptedException{
        placeSetting.put(p);
    }

    public void run(){
        for(String food: Course.food){

            //每次使用者都會從選單中隨機選擇一種食物
            food =Course.randomSelection();
            try{
                //waitPerson提交使用者的訂單
                waitPerson.placeOrder(this,food);

                //表示使用者吃掉食物,假設食物還沒做好。則take()操作會堵塞
                System.out.println(this+" eating "+placeSetting.take());
            }catch(InterruptedException ex){
                System.out.println("Interrupted");
                break;
            }
        }
        System.out.println(this+"finished meal,leaving");
    }

    public String toString(){
        return "Customer "+id+" ";
    }
}

//封裝的侍者類
class WaitPerson implements Runnable{
    private static int counter = 0;
    private final int id = counter++; //侍者編號
    private final Restaurant restaurant;//侍者所屬的飯店

    //無界的堵塞佇列。用來存放廚師已經完畢的食物
    //侍者須要將這些食物送到相應的顧客手上
    LinkedBlockingQueue<Plate> filledOrders = new LinkedBlockingQueue<Plate>();

    public WaitPerson(Restaurant rest){
        this.restaurant = rest;
    }

    //當使用者點了食物以後。侍者提交訂單
    public void placeOrder(Customer cust, String food){
        try{
            //向餐館的訂單佇列中提交一個新訂單
            restaurant.orders.put(new Order(cust,this,food));
        }catch(InterruptedException ex){
            System.out.println("Intrrupted");
        }
    }

    //侍者執行緒的主要作用是不斷的從filledOrders中取出已完畢的食物
    //提交給相應的顧客
    public void run(){
        try{
            while(!Thread.interrupted()){
                //假設佇列為空,則會堵塞
                Plate plate = filledOrders.take();
                System.out.println(this+"received "+plate+" delivering to "+plate.getOrder().getCustomer());
                //將提取的plate提交給相應的顧客
                plate.getOrder().getCustomer().deliver(plate);
            }
        }catch(InterruptedException ex){
            System.out.println(this +"Interrupted");
        }
    }
    public String toString(){
        return "waitPerson "+id+" ";
    }
}

//廚師類
class Chef implements Runnable{
    private static int counter = 0;
    private final int id = counter++;//廚師編號
    private final Restaurant restaurant ;//廚師相應的餐館
    private  Random rand = new Random(47);
    public Chef(Restaurant rest){
        restaurant = rest;
    }

    //廚師執行緒的主要任務是從飯店的訂單佇列提取訂單,然後完畢當中的食物
    //再將完畢以後的plate提交給相應的侍者的filledOrders佇列
    public void run(){
        try{
            while(!Thread.interrupted()){
                //從訂單佇列中取出訂單,假設沒有訂單則會堵塞
                Order order = restaurant.orders.take(); 
                String food = order.item();//取得該訂單所需的食物
                //模擬準備這樣的食物所需的時間
                TimeUnit.MILLISECONDS.sleep(rand.nextInt(500));
                Plate plate = new Plate(order,food);
                //將完畢的plate交給相應的waitPerson
                order.getWaitPerson().filledOrders.put(plate);
            }
        }catch(InterruptedException ex){
            System.out.println(this+"Interrupted");
        }
        System.out.println(this +"off duty");
    }
    public String toString(){
        return "Chef "+id+" ";
    }
}

//飯店類
class Restaurant implements Runnable{
    //飯店的侍者佇列
    private ArrayList<WaitPerson> waitPersons = new ArrayList<WaitPerson>();
    //飯店的廚師佇列
    private ArrayList<Chef> chefs = new ArrayList<Chef>();
    private ExecutorService exec = Executors.newCachedThreadPool();
    private static Random rand = new Random(47);
    //飯店的訂單佇列
     BlockingQueue<Order> orders = new LinkedBlockingQueue<Order>();

    public Restaurant(ExecutorService exe,int nWaitPerson,int nChef){
        exec = exe;
        //預先為飯店分配好侍者和廚師
        for(int i=0;i<nWaitPerson;i++){
            WaitPerson waitPerson = new WaitPerson(this);
            waitPersons.add(waitPerson);
            exec.execute(waitPerson);
        }
        for(int i=0;i<nChef;i++){
            Chef chef = new Chef(this);
            chefs.add(chef);
            exec.execute(chef);
        }
    }

    //飯店任務主要是隔一段時間就產生一個顧客,併為這個顧客分配一個服務的侍者
   public void run(){
       try{
           while(!Thread.interrupted()){
               WaitPerson wp = waitPersons.get(rand.nextInt(waitPersons.size()));
               Customer c = new Customer(wp);
               exec.execute(c);
               TimeUnit.MILLISECONDS.sleep(100);
           }
       }catch(InterruptedException ex){
           System.out.println(this+"Interrupted");
       }
       System.out.println("Restaurant closing");
   }
}

public class RestaurantWithQueues {

    public static void main(String[] args) throws Exception{
        ExecutorService exec = Executors.newCachedThreadPool();

        //指定一個五個侍者。2個廚師的飯店
        Restaurant restaurant = new Restaurant(exec,5,2);
        exec.execute(restaurant);
        System.out.println("Press 'Enter' to quit");
        System.in.read();
        exec.shutdownNow();
    }
}

三.汽車裝配工廠模擬
問題描寫敘述:模擬一條汽車生產線;汽車的生產過程首先是生產底盤。然後在底盤上裝配好發動機,動力傳動系統,車輪,然後一輛車就生產完畢啦。

模擬思路:封裝Car類表示汽車,這個類裡同一時候包括了構建汽車的幾個方法;封裝ChassisBuilder類表示建造底盤的類。封裝Assembler類表示組合其他部分的類。這個類
負責呼叫不同的機器人來組裝汽車不同的部分;封裝Robot類表示抽象的機器人,每一個機器人都會屬於一個RobotPool,同一時候會關聯到一個Assembler(組裝工作),當工作完畢以後這個聯絡就會被取消掉;同一時候還會繼承Robot實現詳細的機器人類。封裝RobotPool類來管理全部的Robot,Assember須要機器人則從中呼叫。

更詳細的思路見以下的程式碼。

詳細實現程式碼例如以下:


package lkl;

import java.util.HashSet;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

//封裝Car類表示汽車
class Car1{
    private final int id;//汽車編號
    //表示開始時汽車各部分都還沒組裝好
    private boolean engine = false ,driveTrain = false, wheels = false;
    public Car1(int id){
        this.id = id;
    }
    public Car1(){
        id = -1;
    }
    public synchronized int getId(){
        return id;
    }
    //以下是組裝汽車的步驟
    //這裡通過設定指定的標記為true。表示完畢了相應的步驟
    public synchronized void addEngine(){
        engine = true;
    }
    public  synchronized void addDriveTrain(){
        driveTrain = true;
    }
    public synchronized void addWheels(){
        wheels = true;
    }
    public synchronized String toString(){
        return "Car "+id+" ["+" engine: "+engine+" driveTrain: "+driveTrain+" wheels: "+wheels+" ]";
    }
}

//封裝的汽車佇列,是一個堵塞佇列
class CarQueue extends LinkedBlockingQueue<Car1>{};

//建造底盤的類
//建好底盤以後就將放入相應的堵塞佇列中,供後面的執行緒使用
class ChassisBuilder implements Runnable{
    private CarQueue carQueue; //存放建好底盤的汽車
    private int counter = 0;
    public ChassisBuilder(CarQueue queue){
        carQueue = queue;
    }
    //執行緒的主要任務就是生成汽車底盤,放入堵塞佇列中
    public void run(){
        try{
            while(!Thread.interrupted()){
                TimeUnit.MILLISECONDS.sleep(400);
                Car1  c = new Car1(counter++);
                System.out.println("ChassisBuilder created "+c);
                carQueue.put(c);
            }
        }catch(InterruptedException ex){
            System.out.println("ChassisBuilder interrpted");
        }
        System.out.println("ChassisBuilder off");
    }
}

//組裝類,通過呼叫機器人在建好的底盤上組裝其他部分
class Assembler implements Runnable{
    //分配記錄裝好底盤的Car和已經完畢組裝號的Car
    private CarQueue chassisQueue,finishedQueue;

    private Car1 car; //正在組裝的Car
    private CyclicBarrier barrier = new CyclicBarrier(4);
    private RobotPool robotPool;
    public Assembler(CarQueue cq,CarQueue fq,RobotPool rt){
        chassisQueue = cq;
        finishedQueue = fq;
        robotPool = rt;
    }
    public Car1 getCar(){
        return car;
    }
    public CyclicBarrier getBarrier(){
        return barrier;
    }

    //執行緒的主要任務就是負責呼叫機器人來組裝Car
    //注意這裡使用了CyclicBarrier來一輛車完畢裝好以後才幹繼續組裝下一輛
    public void run(){

        try{
            while(!Thread.interrupted()){
                //假設底盤還沒有生成則會堵塞
                car = chassisQueue.take();
                //以下會僱傭各個型別的robot去組裝這輛汽車

                robotPool.hire(EngineRobot.class,this);
            //  System.out.println("test");
                robotPool.hire(DriveTrainRobot.class,this);
                robotPool.hire(WheelsRobot.class,this);

                barrier.await(); //假設上面的組裝還沒完畢,則會堵塞在這裡;這樣能夠保證一輛車組裝完以後再組裝下一輛車
                finishedQueue.put(car); //將組裝完畢的車加入佇列
            }
        }catch(Exception ex){
            System.out.println("Assemble Interrupted");
        }
        System.out.println("Assemble off");
    }
}

//將組裝好的汽車輸出進行檢查
class Reporter implements Runnable{
    private CarQueue carQueue;
    public Reporter(CarQueue carQueue){
        this.carQueue = carQueue;
    }

    //執行緒的主要任務是將組裝完畢的汽車列印出來
    public void run(){
        try{
            while(!Thread.interrupted()){
                System.out.println(carQueue.take());
            }
        }catch(InterruptedException ex){
            System.out.println("reporter interrupted");
        }
    }
}

//負責組裝工作的機器人類。是一個抽象類
//以下會有各種機器人的詳細實現
abstract class Robot implements Runnable{
    private RobotPool robotPool;
    public Robot(RobotPool pool){
        robotPool = pool;
        robotPool.add(this); //將自己加入管理池中去
        //robotPool.pool.add(this);
    }
    protected Assembler assembler; //該機器人服務的組裝線
    //關聯到指定的組裝線
    public Robot assignAssembler(Assembler am){
        assembler = am;
        return this;
    }
    private boolean engage = false; //是否在幹活
    //讓機器人幹活
    public synchronized void engage(){
        engage = true;
        notifyAll();
    }

    //由子類實現的抽象方法。每一個子類的行為都不一樣
    abstract protected void performService();

    public void run(){
        try{
            powerDown(); //假設沒有組裝線僱傭這個機器人,則執行緒在此堵塞
            while(!Thread.interrupted()){
                performService();//幹活
                assembler.getBarrier().await(); //表示自己的活已經幹完
                powerDown();
            }
        }catch(Exception ex){
            System.out.println("Exception");
        }
    }
    private synchronized void powerDown() throws Exception{
        engage = false;
        assembler = null ;//解除和裝配線的聯絡
        robotPool.release(this);
        while(engage==false){//沒有活幹時掛起
            wait();
        }
    }
    public String toString(){
        return getClass().getName();
    }
}

//裝配發動機的機器人
class EngineRobot extends Robot{
    public EngineRobot(RobotPool pool){
        super(pool);
    }
    protected void performService(){
        System.out.println(this+" installing engine");
        assembler.getCar().addEngine();
    }
}

//裝配傳動系統的機器人
class DriveTrainRobot extends Robot{
    public DriveTrainRobot(RobotPool pool){
        super(pool);
    }
    protected void performService(){
        System.out.println(this+" installing driveTrain");
        assembler.getCar().addDriveTrain();;
    }
}

//裝配輪子的機器人
class WheelsRobot extends Robot{
    public WheelsRobot(RobotPool pool){
        super(pool);
    }
    protected void performService(){
        System.out.println(this+" installing Wheels");
        assembler.getCar().addWheels();
    }
}

//集中管理全部的機器人
class RobotPool{
    public HashSet<Robot> pool = new HashSet<>();
    public synchronized void add(Robot r){
        pool.add(r);
        notifyAll();
    }
    public synchronized void hire(Class<?extends Robot>robotType,Assembler d) throws Exception{
        for(Robot r: pool){//找到合適品種的機器人。假設找不到則等待再遞迴尋找
                if(r.getClass().equals(robotType)){
                pool.remove(r);
                r.assignAssembler(d);//關聯生產線
                r.engage();//讓機器人幹活
                return ;
             }
         }
        wait();//當前沒有多餘的機器人則等待直到有空暇的再遞迴搜尋
        hire(robotType,d);//遞迴
    }
    public synchronized void release(Robot r){
        add(r);
    }
}

public class CarBuilder {
public static void main(String[] args) throws Exception{
    CarQueue chassisQueue = new CarQueue(),
                       finishedQueue = new CarQueue();
    ExecutorService exec = Executors.newCachedThreadPool();

    //依次啟動各個機器人。生產線
    RobotPool robotPool = new RobotPool();
    exec.execute(new EngineRobot(robotPool));
    exec.execute(new DriveTrainRobot(robotPool));
    exec.execute(new WheelsRobot(robotPool));
    exec.execute(new Assembler(chassisQueue,finishedQueue,robotPool));
    exec.execute(new Reporter(finishedQueue));
    exec.execute(new ChassisBuilder(chassisQueue));
    TimeUnit.SECONDS.sleep(7);
    exec.shutdownNow();
}
}

相關文章