This Java program simulates a post office scenario with postal workers, customers, lines and types of service. The idea behind this small project was to flex Java’s thread muscles in the use of semaphores and mutexes. The program requires two command line arguments, the first for the number of customers and the second for the number of postal workers. These give the simulation a lot of various loads placed on lines and service times.
The program is rather large, so instead of embedding it all we will look at sections and I will provide the entire source for download at the bottom of the page.
////////////////////////////////
// PROCESS CREATION //
////////////////////////////////
// Process Creation: notify the system that the store creation is beginning.
System.out.println("\n\nPost Office open for business, simulating "+ NUMCUSTOMERS +" customers and "+ NUMPOSTALWORKERS +" postal workers\n");
PostOfficeSimulator.openForBusiness = true; //- This variable is used by MeasureTaker.
// Process Creation: declare our postal worker objects and threads.
PostalWorker pwThr[] = new PostalWorker[NUMPOSTALWORKERS];
Thread postalWorkerThreads[] = new Thread[NUMPOSTALWORKERS];
// Process Creation: declare our customer objects and threads.
Customer cThr[] = new Customer[NUMCUSTOMERS];
Thread customerThreads[] = new Thread[NUMCUSTOMERS];
// Process Creation: declare and create our MeasureTaker object and its thread.
MeasureTaker mt = new MeasureTaker();
Thread mThr = new Thread(mt);
mThr.start(); //- Begin running our MeasureTaker thread.
// Process Creation: create our PostalWorker objects and initiate their threads.
for( i = 0; i < NUMPOSTALWORKERS; ++i )
{
pwThr[i] = new PostalWorker(i,
max_capacity,
service_counter,
cust_ready,
service_finished,
leave_service_counter,
mutex_inner,
mutex_greet);
postalWorkerThreads[i] = new Thread( pwThr[i] ); //- Save the PostalWorker as a single thread.
postalWorkerThreads[i].start(); //- Initiate the thread.
}
// Process Creation: create our Customer objects and initiate their threads.
for( i = 0; i < NUMCUSTOMERS; ++i )
{
cThr[i] = new Customer(i,
max_capacity,
service_counter,
cust_ready,
service_finished,
leave_service_counter,
mutex_inner,
mutex_outer,
mutex_greet);
customerThreads[i] = new Thread( cThr[i] ); //- Save the Customer as a single thread.
customerThreads[i].start(); //- Initiate the thread.
}
////////////////////////////////////////////////////////////////////////////////////////
// At this point the threads are all running in the background. //
// Customers run on a linear progression, PostalWorkers and MeasureTaker are looping. //
////////////////////////////////////////////////////////////////////////////////////////
This initial piece of code really highlights the simulation as it is initialized. It demonstrates that the postal workers and customers are threads, each acting independently of one another to achieve realistic simulation. Another thread object you may notice is the MeasureTaker object. This object acts as a simple line monitor that collects data for us to print about the line of the post office.
Lets take a look at what the customer does by viewing the “run” method of the Customer class:
//- Thread operation.
public void run()
{
//////////////////////////////////////////////////////////
//- wait(max_capacity);
//- Wait until customer can enter the line.
try{
max_capacity.acquire(); // decrease line availability
}catch (InterruptedException e){}
//- Sleep .5 seconds per customer before entering the store.
//- Use the customer id as a multiple of the sleep time.
try{
Thread.sleep( ( PostOfficeSimulator.serviceSleep(0) * (num + 1) ) );
}catch(InterruptedException e){}
//- Increase current global line count for MeasureTaker.
PostOfficeSimulator.line_count++;
//////////////////////////////////////////////////////////
//- enter_post_office();
System.out.println( "Customer " + num + " enters the store." );
//////////////////////////////////////////////////////////
//- wait(service_counter);
//- Wait until a spot at the postal worker's counter has opened.
try{
service_counter.acquire(); // take a place at the counter
}catch (InterruptedException e){}
//////////////////////////////////////////////////////////
//- approach_service_counter();
//- A spot at the counter has opened, now the customer can approach.
System.out.println( "Customer " + num + " approaches the counter " + PostOfficeSimulator.serviceDisplay(0, this.service_needed) + "." );
//////////////////////////////////////////////////////////
//// wait(mutex_outer);
//// MUTEX OUTER: Limit other customers from writing to our global customer data
//// START until a postal worker has read this customer data. This represents
//// assigning a postal worker to a particular customer.
try{
mutex_outer.acquire();
}catch (InterruptedException e){}
//////////////////////////////////////////////////////////
//// wait(mutex_inner);
//// MUTEX INNER: Ensure that the postal worker assigned cannot gain access
//// START to the global customer data until the customer has written
//// to it.
try{
mutex_inner.acquire();
}catch (InterruptedException e){}
//- Set global customer information.
PostOfficeSimulator.cust_id = this.num;
PostOfficeSimulator.cust_service = this.service_needed;
//////////////////////////////////////////////////////////
//- signal(cust_ready);
//- This customer is now available to be served by a postal worker.
cust_ready.release();
//////////////////////////////////////////////////////////
//// signal(mutex_inner);
//// MUTEX INNER: Release the postal worker to read the global customer information
//// END written by the customer now that it is complete.
mutex_inner.release();
//////////////////////////////////////////////////////////
//- wait(mutex_greet);
//- Wait until greeted by postal worker before allowing another customer to globalize thier data.
try{
mutex_greet.acquire();
}catch (InterruptedException e){}
//////////////////////////////////////////////////////////
//// signal(mutex_outer);
//// MUTEX OUTER: Other customers may now access the global customer data since this
//// END customer has safely written to it, and their assigner postal worker
//// has safely read from it.
mutex_outer.release();
//////////////////////////////////////////////////////////
//- wait(service_finished);
//- Wait until the assigned postal worker has completed the service desired.
try{
service_finished.get(this.num).acquire();
}catch (InterruptedException e){}
//////////////////////////////////////////////////////////
//- leave_service_counter();
//- signal(leave_service_counter);
//- The customer has received the service from the postal worker and can leave a counter space.
System.out.println( "Customer " + num + " leaves the counter." );
//- Notify postal worker that customer is leaving.
leave_service_counter.release();
//////////////////////////////////////////////////////////
//- exit_post_office();
//- Decrease our global line count.
PostOfficeSimulator.line_count--;
//////////////////////////////////////////////////////////
//- signal(max_capacity);
//- Leave the store's line and make room for another created customer.
System.out.println( "Customer " + num + " leaves the store." );
max_capacity.release();
}
The customer will wait until there is an open spot in the line, signified by the semaphore “max_capacity.acquire”. Once one becomes available the customer takes that spot. From there the customer waits on another semaphore representing the an open service counter. Once a spot is available at the service counter the customer will approach the service counter and await the postal worker to accept them as their next customer.
Once the postal worker has accepted the customer, the customer has not gained control over a shared variable for the customer’s information. This prevents other customers from overwriting this data for other postal workers to use until the customer’s postal worker has read that information. Then the customer simply waits on the postal worker to complete the service. Then the customer will release control over their service counter and then release control over a position in line.
Let’s take a look at the postal worker for a comparison:
//- Thread operation.
public void run()
{
//- Loop the postal worker until the system exits, always accepting and helping customers.
while(true)
{
//////////////////////////////////////////////////////////
//- wait(cust_ready);
//- Wait until a customer is ready to be served.
System.out.println( "Postal worker " + num + " prepares for customer." );
try{
cust_ready.acquire();
}catch (InterruptedException e){}
//////////////////////////////////////////////////////////
//- wait(mutex_inner);
//- Postal worker cannot read the customer information until the Customer object has safely written it.
try{
mutex_inner.acquire();
}catch (InterruptedException e){}
//- Fetch the assigned global customer information.
this.customer_id = PostOfficeSimulator.cust_id;
this.customer_service = PostOfficeSimulator.cust_service;
System.out.println( "Postal worker " + num + " greets customer "+ this.customer_id +" and begins working." );
//////////////////////////////////////////////////////////
//- signal(mutex_greet);
//- Let the customer know that the postal worker has successfully read the customer data.
mutex_greet.release();
//////////////////////////////////////////////////////////
//- signal(mutex_inner);
//- Allow other customers to write to the global customer data.
mutex_inner.release();
//////////////////////////////////////////////////////////
//- perform_service();
//- Sleep for the proper service duration, then notify of its completion.
try {
Thread.sleep(PostOfficeSimulator.serviceSleep(this.customer_service));
}catch(InterruptedException e){}
System.out.println( "Postal worker " + num + " "+ PostOfficeSimulator.serviceDisplay(1, this.customer_service) +" customer "+this.customer_id+"." );
//////////////////////////////////////////////////////////
//- signal(service_finished);
//- Notify the customer that the service has been fully rendered.
service_finished.get(this.customer_id).release();
//////////////////////////////////////////////////////////
//- wait(leave_service_counter);
//- Wait for the customer to leave the counter.
try{
leave_service_counter.acquire(); // decrease line availability
}catch (InterruptedException e){}
//////////////////////////////////////////////////////////
//- signal(service_counter);
//- Allow another customer to approach the counter since it is now vacant.
service_counter.release();
}
}
The postal worker has less required actions than that of a customer because they are not involved with obtaining access to the line or other service counters. As a result the semaphore of importance to the postal worker is the “cust_ready” variable that notifies the customers that the postal worker is available for interaction.
After this we see a reflection of the exchange from the Customer.run() method where the postal worker and the customer are exchanging data while protecting it from other postal workers. This passed information contains the type of service to be rendered by the postal worker. A switch statement in our main application is used by the postal worker to sleep the CPU during the duration of the service.
Once the service is complete the customer is released from awaiting the service and a new customer is waited upon.
Lets take a look at a sample output given arguments “10 2″, 10 customers and 2 postal workers.
Post Office open for business, simulating 10 customers and 2 postal workers Postal worker 0 created. Postal worker 1 created. Postal worker 0 prepares for customer. Postal worker 1 prepares for customer. Customer 0 created. Customer 1 created. Customer 2 created. Customer 3 created. Customer 4 created. Customer 5 created. Customer 6 created. Customer 7 created. Customer 8 created. Customer 9 created. Customer 0 enters the store. Customer 0 approaches the counter to mail a letter. Postal worker 0 greets customer 0 and begins working. Customer 1 enters the store. Customer 1 approaches the counter to buy stamps. Postal worker 1 greets customer 1 and begins working. Customer 2 enters the store. Customer 3 enters the store. Postal worker 0 sends a letter for customer 0. Postal worker 1 sells stamps to customer 1. Customer 0 leaves the counter. Customer 1 leaves the counter. Postal worker 0 prepares for customer. Customer 2 approaches the counter to mail a package. Postal worker 1 prepares for customer. Customer 1 leaves the store. Customer 0 leaves the store. Joined customer 0. Postal worker 0 greets customer 2 and begins working. Customer 3 approaches the counter to mail a letter. Joined customer 1. Postal worker 1 greets customer 3 and begins working. Customer 4 enters the store. Customer 5 enters the store. Postal worker 1 sends a letter for customer 3. Customer 3 leaves the counter. Customer 3 leaves the store. Customer 4 approaches the counter to pickup a package. Postal worker 1 prepares for customer. Postal worker 1 greets customer 4 and begins working. Customer 6 enters the store. Postal worker 0 sends a package for customer 2. Customer 2 leaves the counter. Customer 2 leaves the store. Customer 5 approaches the counter to mail a package. Joined customer 2. Postal worker 0 prepares for customer. Joined customer 3. Postal worker 0 greets customer 5 and begins working. Customer 7 enters the store. Customer 8 enters the store. Customer 9 enters the store. Postal worker 0 sends a package for customer 5. Customer 5 leaves the counter. Customer 5 leaves the store. Customer 6 approaches the counter to mail a package. Postal worker 0 prepares for customer. Postal worker 0 greets customer 6 and begins working. Postal worker 1 gets a package for customer 4. Customer 4 leaves the counter. Customer 4 leaves the store. Customer 7 approaches the counter to buy stamps. Postal worker 1 prepares for customer. Joined customer 4. Postal worker 1 greets customer 7 and begins working. Joined customer 5. Postal worker 1 sells stamps to customer 7. Customer 7 leaves the counter. Customer 7 leaves the store. Customer 8 approaches the counter to buy stamps. Postal worker 1 prepares for customer. Postal worker 1 greets customer 8 and begins working. Postal worker 0 sends a package for customer 6. Customer 6 leaves the counter. Customer 6 leaves the store. Customer 9 approaches the counter to mail a letter. Postal worker 0 prepares for customer. Joined customer 6. Postal worker 0 greets customer 9 and begins working. Joined customer 7. Postal worker 1 sells stamps to customer 8. Customer 8 leaves the counter. Customer 8 leaves the store. Postal worker 1 prepares for customer. Joined customer 8. Postal worker 0 sends a letter for customer 9. Customer 9 leaves the counter. Customer 9 leaves the store. Postal worker 0 prepares for customer. Joined customer 9. Post Office closed. Number of Postal Workers: 2 Number of Customers: 10 Average Line Length (double): 3.0 Average Line Length (int): 3 Maximum Line Length: 6
Some provided metric data is calculated based upon the information taken by the MeasureTaker class. It gives us insight into the load times and weights of the line. Download the Eclipse project archive and try it out for yourself.
Download: PostOfficeSimulator.zip
This concept can be expanded into a lot of various simulators, and is a good start to some simple A.I. for a game environment. You could interact with the customers and postal workers through input manipulating the flow of service dynamically.


