We'll only need two classes:
- Queue.java
- Supermarket.java
First we'll review the Queue.java class, for which we need to remember the concept of queue: "A Queue is a linear data structure where the first element is inserted from one end called REAR and subtracted/deleted from the other end called FRONT". Another way of explaining this is: "the first item that we insert should be the first one that we remove."
Now in our Queue.java class we have a fully functional queue with all its methods that I'll explain bellow in the comments:
class QueueArray { protected int maxSize; protected long[] queArray; protected int front; protected int rear; protected int nItems; /* We initilize this queue as an array because we want to practice how a queue works but this is completly optional as we have the Java Queue interface from java.util. Also, although at the end, a linear queue and a circular queue are practically the same, this queue is an strictly linear one, because custumers make a line strictly one after the other.*/ public QueueArray(int s) { maxSize = s; queArray = new long[maxSize]; front = 0; rear = -1; nItems = 0; } /*We don't verify if the queue is full as this is implementation specific so becarefull by checking before inserting.*/ public void insert(long j) { queArray[++rear] = j; nItems++; } /*We don't verify if the queue is empty as this is implementation specific so becarefull by che-.king before removing.*/ public long remove() { long temp = queArray[front++]; nItems--; return temp; } /*Simply allows you to see the element in the front without removing it.*/ public long peek() { return queArray[front]; } /*Checks if there are no items left in the queue*/ public boolean isEmpty() { return (nItems==0); } /*Checks if there is no space available in the queue*/ public boolean isFull() { return (nItems==maxSize); } /*Returns the number of elements in the queue*/ public int size() { return nItems; } /*Displays the queue from left to right: [front]...[2][3][4]....[rear]*/ public void displayQueue() { int disp = front; if(!this.isEmpty()) while(disp <= rear) System.out.print("[" + this.queArray[disp++] + "]"); else System.out.println("Empty queue"); System.out.println(""); } }
The following would be our main function:
public static void main(String[] args) { /*********** Testing ***********************/ System.out.println("SUPER MARKET SIMULATOR:"); Supermarket supermarket = new Supermarket("SuperMarket_1"); supermarket.start();//Starts main thread: the supermarket /**********************************************************************/ System.out.println("\t\t\tFinishing main thread..."); }
Latter we'll discuss an implementation without this class.
Then we have the Supermarket.java class that extends from thread, this because the supermarket is a thread itself and it'll execute a group of threads that will be the cashiers.
This is where we put in practice asynchrony: When you run this application and wait for a while (5 minutes or so) you'll able to see that as the supermarket (thread) interacts with its queues (inserting customers), each one of the eight cashiers (threads) simultaneously interacts with the same queues (removing customers).
//To use the java.util.Queue interface uncomment the following lines(A): /* import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; */ public class Supermarket extends Thread { private final int Ncashiers = 8; // number of cashiers (and queues) private final int custPerLine = 20; // size of each queue (customers per line) private final int hrs = 15; //hours that the supermarket remains open private final int totalTimeInSecs = hrs*1000*60*60; //time in seconds that the supermarket remains open private long clock; //variable to count elapsed time. // To use the java.util.Queue interface replace with: private final Queue<Integer>[] lines; (B)/>/>/>/> private final QueueArray[] lines; // where the customers are going to queue. private final Thread[] checkers; // an array of threads that represents the cashiers attending to the customers. private boolean closing; //flag to close the supermarket private final long NewCustTimeMax = 10; //Max time (in seconds) before a new cutomer queues. private final long NewCustTimeMin = 5; //Min time (in seconds) before a new cutomer queues. private final long groceryPass = 1000; // average time that takes a cashier passing each item (1sec). private final long maxGroceries = 200; // max amount of groceries a customer takes private final long otherTransac = 1000*30; //an average of half a minute in other transactions such as withdraws. public SuperMarket(String string) { super(string); clock = 0; this.lines = new QueueArray[Ncashiers]; closing = false; this.checkers = new Thread[Ncashiers]; } @Override public void run() { System.out.println("\t\t\tStarting supermarket thread..."); for (int i = 0; i < Ncashiers; i++) { final int index; index = i;//this is possible because we only initilize index with one value for each iteration. // To use the java.util.Queue interface replace with: lines[i] = new LinkedList<>(); (C) lines[i] = new QueueArray(this.custPerLine); //queues checkers[i] = new Thread() //cashiers { @Override public void run() { System.out.println("\t\t\tStarting cashier " + (index+1) + " thread..."); while(!closing) { if( !lines[index].isEmpty() ) try { /*The thread waits as it simulates the cashier chargin the current customer, then it removes it.*/ Thread.sleep(lines[index].peek() * groceryPass + otherTransac ); System.out.println("A customer just left cashier " + (index+1) + ", with " + lines[index].remove() + "items." ); } catch (InterruptedException ex) { System.out.println("For some reason cashier " + (index+1) + "couldn't charge," + "hints about the reason: \n" + ex); } else try { /**The cashier waits for the arrival of a new customer if their queue is empty **/ Thread.sleep(NewCustTimeMin*1000); } catch (InterruptedException ex) { System.out.println("For some reason cashier " + (index+1) + "couldn't wait for a new customer," + "hints about the reason: \n" + ex); } } } }; checkers[i].start(); } int best; long groseries; while(!closing) { best = this.pickTheBest(); if( !this.lines[best].isFull() ) try { /**There is a time from NewCustTimeMin to NewCustTimeMax (in seconds) before a new customer queues**/ long timePassed = (long)Math.random()*NewCustTimeMax*1000+NewCustTimeMin*1000; Thread.sleep(timePassed); clock+=timePassed;//counting the time that's passed groseries = (long)(Math.random()*this.maxGroceries) + 1 ;//Queuing customers with random amount of groceries this.lines[best].insert( groseries ); System.out.println("A customer just queued at cashier " + (best+1) + ", with " + groseries + "items.");//displays where the last customer has queued //displays all the cashiers for (int i = 0; i < this.Ncashiers; i++) if(!lines[i].isEmpty()) { System.out.print((i+1)+". "); /*Line to modify if you want to use the java.util.Queue interface*/ this.lines[i].displayQueue();/* To use the java.util.Queue interface replace with: System.out.println( Arrays.toString( this.lines[i].toArray() ) );(D)*/ } System.out.println(""); closing = clock>totalTimeInSecs; //checks if it's time to close the supermarket } catch (InterruptedException ex) { System.out.println("For some reason cashier " + (best+1) + "couldn't wait for a new customer," + "hints about the reason: \n" + ex); } } } /*With this function customer decides which line is the best to queuing by picking the one with the lowest amount of customers queued*/ private int pickTheBest() { int temp = this.lines[0].size(), pos = 0; for (int i = 0; i < this.lines.length; i++) if(this.lines[i].size() < temp) { temp = this.lines[i].size(); pos = i; } return pos; } }
The supermarket and the cashiers threads execute most of its code in a loop that will end when the variable "clock" reaches the value of the "totalTimeInSecs" variable.
Variables "NewCustTimeMax" and "NewCustTimeMin" are important for you to see an analyze carefully how the simulator works so feel free to adjust them if you feel that the simulator goes too fast or too slow.
When running the whole application you'll be able to see that the main thread finishes but before that it has already called the start() method of our variable supermarket so the supermarket thread starts.
Then you'll see at the console a format like this:
A customer just queued at cashier 1, with 24 items.
A customer just left cashier 5, with 163 items.
1. [59]
2. [100][145]
3. [112][165]
4. [147][124]
5. [87][163]
6. [153]
7. [39]
8. [2]
Where you can see how many items the next customer is going to take in each one of the queues. When a customer arrives you'll read "A customer just queued at cashier 'a' with 'b' items." If a customer has finished all their transactions you'll see "A customer just left cashier a with b items.".
You'll see the eight queues printed each time a customer arrives to any one of them but if a customer leaves you'll only se the message previously explained.
Finally, you'll find four specific lines (A, B, C and D) at the Supermarket.class that you need to modify if you want to use the java.util.Queue interface instead the QueueArray.java class proposed in this tutorial.
Signed (SHA256): 2e35bb88a90252a391c8e6c2b4f6c204b0536b5b06211bdf7e3cfc2140272a1d