Page 1 of 1

A supermarket simulator to practice queues, threads and asynchrony Rate Topic: -----

#1 Aisaac23   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 5
  • Joined: 28-April 19

Posted 17 November 2019 - 07:59 PM

In this tutorial we're going to put in practice the basics of queues, threads and asynchrony (asynchronized threads).

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

Is This A Good Question/Topic? 0
  • +

Replies To: A supermarket simulator to practice queues, threads and asynchrony

#2 astonecipher   User is offline

  • Senior Systems Engineer
  • member icon

Reputation: 3000
  • View blog
  • Posts: 11,549
  • Joined: 03-December 12

Posted 27 November 2019 - 01:54 PM

Tweaks

import java.util.concurrent.*;
public class TestThread {
	public static void main (String[] args) throws ExecutionException, InterruptedException {
		System.out.println("begin");
		
		ExecutorService pool = Executors.newFixedThreadPool(1);
		ExecutorCompletionService poolAsync = new ExecutorCompletionService(pool);
		
		Task<String> reusableTask = new Task<String>();
		
		poolAsync.submit(reusableTask); //I could have inlined the Callable if I wanted
		Future<String> future = poolAsync.take();
		
		System.out.println(future.get());
		System.out.println(future.isDone());
		
		poolAsync.submit(reusableTask);
		//future = poolAsync.take();
		
		//System.out.println(future.get());
		//System.out.println(future.isDone());
		
		System.out.println("hello");
		poolAsync.take();
		System.out.println("hello2");
		//pool.shutdown();
	}
}

class Task<T> implements Callable {

	@Override
	public T call() {
		System.out.println("ran");
		for (int i=0; i<9999999; i++); 
		return (T) ("finally done");
	}
}


Was This Post Helpful? 0
  • +
  • -

#3 Aisaac23   User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 5
  • Joined: 28-April 19

Posted 05 December 2019 - 08:02 AM

Hi astonecipher,

I don't get what you mean. Do you mean that it's better to use the async libraries that you mentioned? Could you please explain further?

Thanks in advance :)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1