Java Multithreading Project – Burrito Restaurant Simulation with Semaphores

This Java application simulates a busy 24/7 burrito restaurant called Burrito Brothers, using multithreading and the Semaphore class for synchronization. The system models customers and servers as individual threads. Customers enter the restaurant (if it’s not at full capacity), place orders ranging from 1 to 20 burritos, and are served based on the shortest order first. Servers can prepare up to 3 burritos at a time and share a single register for payment. The simulation showcases concurrent processing, resource sharing, and proper synchronization techniques using semaphores.



For this problem you must use Java's semaphore class. Your  implementation for this problem must only use the semaphore methods to  control the concurrency of your solution (IE. acquire and release).  Consider a small take-out food restaurant called Burrito Brothers that  is open 24 hours, seven days a week. This very popular establishment  offers a VERY-TASTY burrito. There are three servers that own a private  beef area, a cheese area, and a tortilla area (IE. Each server has their  own ingredients in an infinite supply). Additionally, there are three  shared counter locations, a shared cash register and a shared waiting  area that can accommodate up to 15 customers. By law the shop has a  maximum customer capacity of 15. A customer cannot enter the shop if it  is filled to capacity. If the there is room a customer will gain access  to the shop. Each customer will enter the shop with an order of one to  20 burritos. As soon as a server is free, the customer that has the  shortest order is served next. A server is either servicing a customer  or waiting. Each server will make (at most) three burritos at time for a  given customer. Once a server has obtained all ingredients, a burrito  can be made. When a customer's entire order is finished, the customer  pays a cashier and leaves the shop. Since there is only one cash  register, only one patron may pay at a time. However, in the event that a  customer's entire order has not been filled by the server at the  completion of the current counter visit the customer must reenter the  waiting area. The waiting area is organized by the shortest order next.  Implement a solution to this problem as a Java application using the  Java semaphore class for synchronization. Your zip file must expand into  a single directory and your application must compile at the command  prompt using javac *.java. Output must include the arrival of each  customer and each state transition: leaving full shop, entering shop  with an order of m burritos, customer standing, getting service from  server n, paying, and leaving. Each customer is represented by a  corresponding thread and each server in your implementation is also  represented by a unique Java thread. All customers and servers exist  within a single application. The system must compile using the command:  javac *.java. The system must run using the command: java Burrito.

 

Answer

Burrito.java

import java.util.Scanner;

public class Burrito

{

  protected static int NoOfCustomers=0;

  protected static int NoOfServers=0;

  protected static int StorCapacity=0;

  protected static int ArrivalWindowMax=0;

  protected static int CookingSpeed=0;

  protected static int RegisterSpeed;

  protected static boolean advmode;

  static Scanner conIn = new Scanner(System.in);

  public static void main(String args[]) throws Exception

  {     

     System.out.println("Welcome to Burrito Brothers restaurant");

     System.out.println("\nChoose simulator mode: ");

     System.out.println("1: User Mode ");

     System.out.println("2: Test Mode (Advanced) ");

     String  adv="";     

     switch (input())

     {

         case 1: // User Mode

           advmode=false;

           NoOfCustomers=30;

           NoOfServers=3;

           StorCapacity=15;

           ArrivalWindowMax=4000;

           CookingSpeed=1500;

           RegisterSpeed=1000;

         break;

         case 2: // Test Mode

           advmode=true;

           System.out.println("How many CUSTOMERS you want to generate?");

           NoOfCustomers=input();

           System.out.println("How many SERVERS you want to generate?");

           NoOfServers=input();

           System.out.println("Setup store capacity more then 1");           

           while (StorCapacity<2)

           {

              StorCapacity=input();

              if (StorCapacity<2) System.out.println("store capacity must be more then 1");  

           }

           System.out.println("Setup customer generator frequency's upper limit (in MILLISECONDS) ");

           ArrivalWindowMax=input();           

           System.out.println("Setup Cooking time (in MILLISECONDS) for one burrito");

           CookingSpeed=input();

           System.out.println("Setup Register speed (in MILLISECONDS)");

           RegisterSpeed=input();

           adv="|---------- Waiting line and Register line -------------";

         break;         

         default:

           System.out.println("Error in mode choice. Terminating test.");

           return;

     }

     System.out.println("-------------  Customers activity --------------------|-------------------- Servers  activity --------------"+adv);

     for (int i=0; i<NoOfServers; ++i)

     {

        Thread Server = new Thread(new Server(i));        

        Server.start();              

     }

     for (int i=0; i<NoOfCustomers; ++i)

     {

        Thread Customers = new Thread(new Store().getShop());

        Customers.start();  

        try {Thread.sleep((int)(1+ArrivalWindowMax*Math.random()));}

        catch (InterruptedException e) {e.printStackTrace();}

     }  

  }

  static int input()

  {

     int input=0;

     String skip;      // skip end of line after reading an integer

     boolean flag=true; // flag for "Input" loop

     while (flag)        

     {

         if (conIn.hasNextInt())

         {

           input = conIn.nextInt();

           flag=false;

         }

         else

         {

             System.out.println("Error: you must enter an integer.");

             System.out.println("Try again");

              // return;

         }

          skip = conIn.nextLine();

     }

     return input;     

  }

}

Server.java

import java.util.concurrent.TimeUnit;

public class Server implements Runnable

{

  private int serverID;

  private Customer atCounter;

  static private int ServersInStor=Burrito.NoOfServers;

  public Server(int serverID)

  {

     this.serverID=serverID;

  }  

  public void FreeServer()

  {

     try

     {

        //Shop.getShop().semStartServing.acquire();

        Store.getShop().semCounter.acquire();

           atCounter=Store.getShop().Counter(serverID);

        Store.getShop().semCounter.release();

        if (atCounter.getOrderSize()>3)

        {

           atCounter.redOrderSize();  // redusing ordersize by 3 buritos        

           Store.getShop().Cooking(3,serverID);  

           System.out.println("Customer #"+atCounter.getCustID() +" should reenter the line");

           Store.getShop().CheckLine(atCounter,false);  

           Store.getShop().semStartServing.release();

        }                                                           

        else

        {

           Store.getShop().Cooking(atCounter.getOrderSize(),serverID);

           Store.getShop().GoToPay(atCounter);     

           if (!Store.getShop().RegisterLine.isEmpty()&&Store.getShop().semRegister.tryAcquire())

           {

              System.out.println(Store.getShop().space+"Server # "+serverID+" is on register");

              Store.getShop().Register();

              System.out.println(Store.getShop().space+"Server # "+serverID+" has left register");

           }           

        }

     }

     catch (InterruptedException e1) {e1.printStackTrace();}

  }

  public void run()

  {

     boolean working=true;  

     while (working)

     {

        try

        {

           // try to serve customer if no customer in line wait

           if   (Store.getShop().semStartServing.tryAcquire(Burrito.ArrivalWindowMax*Burrito.NoOfServers+100,TimeUnit.MILLISECONDS))

           {

              FreeServer();

           }

           else

           {

              working=false; // closing store

              System.out.println("Server #"+serverID+" finish his job" );

              --ServersInStor;

              if (ServersInStor==0)System.out.println("Store is closed" );

           }

        }

        catch (InterruptedException e) {e.printStackTrace();  }

     }     

  }

}

Store.java

import java.util.LinkedList;

import java.util.concurrent.Semaphore;

public class Store implements Runnable

{

  private static Store Str = new Store();

  private int CustInShop=0;

  protected int CustInLine=0;

  protected int custID=0;

  protected Customer line[]=new Customer[Burrito.StorCapacity];

  protected LinkedList<Customer> RegisterLine = new LinkedList<Customer>();  

  protected Semaphore semCustInStore = new Semaphore(1);

  protected Semaphore semStartServing = new Semaphore(0);

  protected Semaphore semRegister = new Semaphore(1);

  protected Semaphore semRegisterLine = new Semaphore(1);

  protected Semaphore semCounter = new Semaphore(1);

  protected Semaphore semLine = new Semaphore(1);

  protected Semaphore semIngredients = new Semaphore(1);

  String space =String.format("%" + 55 + "s", " ");

  public static Store getShop()

  {

     return Str;

  }

  public void Welcome()

  {     

     try

     {

        semCustInStore.acquire();  // only one customer can enter store at a time.

        ++custID;

        if(CustInShop<Burrito.StorCapacity)

        {              

           ++CustInShop;           

           Customer Cust = new Customer();

           Cust.setCustID(custID);

           System.out.println("Welcome! Cust #"+Cust.getCustID()+" with order of "+Cust.getOrderSize()+" Burritos In");

           CheckLine(Cust,true);              

           semStartServing.release(); // letting server now that there is customer in the store.

           semCustInStore.release(); // now other customer can enter the store

        }

        else

        {

           System.out.println("Too many people inside Cust # "+custID+" hsa left the stor");

           semCustInStore.release(); // now other customer can enter the store

        }

     }

     catch (InterruptedException e1) {e1.printStackTrace();}

  }

  public void CheckLine(Customer Cust, boolean newCust)

  {

     try           

     {

        semLine.acquire();     

           if (CustInLine==0) EnterLine(1,0,Cust);  

           else

           {

              if (CustInLine==1&&!newCust) line[1]=Cust;

              else                       

              {

                 if (CustInLine==1&&newCust)

                 {

                    if (Cust.getOrderSize()>line[0].getOrderSize()) line[1]=Cust;

                    else

                    {

                       line[1]=line[0];

                       line[0]=Cust;

                    }

                 }

                 else

                 {  

                    for (int i=0; i<CustInLine-1; ++i)

                    {

                       if (line[i].getOrderSize()>line[i+1].getOrderSize())

                       {                          

                          EnterLine(CustInLine+1,i+1,Cust);

                          break;

                       }

                       if (i==CustInLine-2) line[i+2]=Cust;

                    }

                 }

              }

           }           

           ++CustInLine;           

        semLine.release();

     }     

     catch (InterruptedException e1) {e1.printStackTrace();}     

  }

  public void EnterLine(int CustInLine, int Sortuntill, Customer Cust)

  {

     if (CustInLine==1) line[0]=Cust;

     else

     {

        for (int i=CustInLine-1; i>Sortuntill; --i)

        {

           if(Cust.getOrderSize()<line[i-1].getOrderSize())

           {

              line[i]=line[i-1]; //moving elements

              if (i==Sortuntill+1)

              {

                 line[i-1]=Cust;

                 break;

              }

           }

           else

           {

              line[i]=Cust;

              break;

           }

        }

     }

  System.out.println("Customer #"+Cust.getCustID()+" with order of "+Cust.getOrderSize()+" burritos waiting in line");  

  }

  public Customer Counter(int serverID)

  {

     Customer atCounter;

     String Line="";

     if (Burrito.advmode)

     {

        for (int i=0; i<Store.getShop().CustInLine; ++i)

           Line=Line+"C"+Store.getShop().line[i].getCustID()+"("+Store.getShop().line[i].getOrderSize()+")-";           

        System.out.println(space+space+"Customers in line:");

        System.out.println(space+space+Line);

     }

     atCounter=line[0];                 //serving first customer in the line

     System.out.println(space+"Server #"+serverID+" serving Customer #"+atCounter.getCustID());                             

     /*test*///Prnt.getPrnt().ServerIsFree(serverID,atCounter);

     for (int i=0; i<CustInLine; ++i)     //moving the line

        line[i]=line[i+1];

     --CustInLine;                    //customer is on counter, not in line anymore     

     line[CustInLine]=null;

     return atCounter;

  }

  public void Cooking(int burritos, int ServerID)

  {

     try

     {

        semIngredients.acquire();

        System.out.println(space+"Server #"+ServerID+" has obtained all ingredients");    

        semIngredients.release();

        System.out.println(space+"Cooking...");

        try {Thread.sleep(burritos*Burrito.CookingSpeed);} //simulating servers work

        catch (InterruptedException e) {e.printStackTrace();}

     }

     catch (InterruptedException e1) {e1.printStackTrace();}

  }

  public void GoToPay(Customer atCounter)

  {

     LinkedList<Customer> RegisterPrnt = new LinkedList<Customer>();

     String AtRegister="";

     try

     {

     semRegisterLine.acquire();

        RegisterLine.addLast(atCounter); //adding customer into register queue

        if (Burrito.advmode)

        {

           RegisterPrnt=RegisterLine;

           for (int i=0; i<RegisterPrnt.size(); ++i)

              AtRegister=AtRegister+"C"+RegisterPrnt.get(i).getCustID()+"<=";

           System.out.println(space+space+"Customers on register:");

           System.out.println(space+space+AtRegister);

        }

     semRegisterLine.release();

     }

     catch (InterruptedException e1) {e1.printStackTrace();}

  }

  public void Register()

  {

     Customer cust;  

     while (!RegisterLine.isEmpty())

     {           

        cust=RegisterLine.pollFirst();

        System.out.println("Customer #"+cust.getCustID()+" is paying...");

        try {Thread.sleep((int)(1+Burrito.RegisterSpeed*Math.random()));} //simulating paying

        catch (InterruptedException e) {e.printStackTrace();}

        System.out.println("Good bye! Customer #"+cust.getCustID() +" is leaving the store");

        --CustInShop;  //customer exits the shop

     }  

     semRegister.release();

  }  

  public void run()

  {

     Welcome();  

  }

}

Customer.java

public class Customer

{

  private int custID;

  private int orderSize;

  public Customer()

  {

     this.orderSize = (int)(1+20*Math.random());     

  }

  protected int getOrderSize()

  {

     return orderSize;

  }

  protected void redOrderSize()

  {

     this.orderSize=this.orderSize-3;

  }

  public int getCustID()

  {

     return custID;

  }

  public void setCustID(int custID)

  {

     this.custID = custID;

  }

}

 

  📩 Need a similar solution? Email me: adel455@hotmail.com


Previous Post Next Post

Comments

Nepali Graphics - Learn design, Animation, and Progrmming