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;
}
}