|
|
`Queue' example program: simulate queuing; demonstrates multi-threaded simulations
Queue.java
/*==============================================================================
Queue.java
This is a simple program that simulates `Queuing problems'. People who are
more familiar with American English rather than British English will perhaps
be familiar with the term `waiting in line' rather than `queuing'. In
Britain the word `queue' is a verb and a noun (we queue, and we join a queue).
Anyway, queueing problems are very important in business. Consider a situation
where you have a telephone helpdesk. There is a person answering calls, and
new people calling all the time. Calls can be held in a queue, but if you
make people wait too long they will get bored and hang up. This will lead
to dissatisfaction with your business. On the other hand, if you employ
more people to answer calls, sometimes they will have nothing to do. The
optimum situation is to get the best compromise between the queue being
empty (i.e., your staff not having anything to do) and the queue being
full (i.e., your customers getting cross). Programs like this one
can be used to investigate queueing experimentally.
From a Java perspective, this program is a demonstration of multithreading.
The main applet creates two new threads of execution; one represents the
`supplier' (that is, the process that puts people on the queue) and the
other the consumer (that is, the process that takes people off the queue.
The supplier and the consumer may work at different speeds. The program
displays the number of people in the queue at any given time, and the
number of times people were turned away from the queue, or the queue
was found to be empty and the consumer had nothing to do.
Although simple, the princples embodied by this applet are very
important. The use of different threads that can communicate with one
another is a very effective way to make programs more responsive.
The applet's display looks like this:
(If you aren't looking at the HTML version of this file, you won't see
the picture)
The way the applet is currently configured, the queueing problem is set up as
follows:
The maximum number of people in the queue is ten. New people arrive
at random time intervals between 0 and 800 seconds. It takes a random length
of time between 0 and 1000 seconds to deal with each person that joins the
queue. This means that the queue will gradually become full, and people will
start being turned away.
To speed things up, I have reduced the time scale by a factor of 1000, that is,
I have used 800 milliseconds rather than 800 seconds. Otherwise it would take
several weeks to get any results.
It is very easy to change these parameters by modifying the values assigned
to the attributes in the constructor.
Kevin Boone, July 1999
==============================================================================*/
import java.applet.Applet;
import java.awt.*;
public class Queue extends Applet
{
/*==============================================================================
Attributes of Queue class
All these attributes are initialized in the constructor
==============================================================================*/
// numberInQueue is the number of people currently waiting in the queue
int numberInQueue;
// queueLimit is the number of people that the queue can accomodate
int queueLimit;
// queueEmpty is the number of times that the consumer tries to deal with a
// person in the queue, but finds the queue empty
int queueEmpty;
// queueFull is the number of times a person tries to join the queue, but
// finds it full
int queueFull;
// itemsProcessed is the total number of people that have joined the queue
// and been dealt with
int itemsProcessed;
// consumer is the object that simulates the removal of people from the queue
Consumer consumer;
// supplier is the object that simulates the addition of people to the queue
Supplier supplier;
// These labels are where the values of the queue properties will be displayed
// The label objects are created in the constructor
Label numberInQueueLabel;
Label queueLimitLabel;
Label queueFullLabel;
Label queueEmptyLabel;
Label itemsProcessedLabel;
/*==============================================================================
Queue constructor
Create the display elements, and the supplier and consumer objects. Then
sets the supplier and consumer running.
==============================================================================*/
public Queue()
{
super();
// Create the consumer and supplier objects, and call
// setQueue to indicate that this is the queue they
// are associated with
consumer = new Consumer();
consumer.setQueue(this);
supplier = new Supplier();
supplier.setQueue(this);
// The display for this program is very simple: it's just
// ten labels arranged in a 5 x 2 grid
setLayout(new GridLayout(5, 2));
add (new Label ("Number in queue"));
numberInQueueLabel = new Label("");
add (numberInQueueLabel);
add (new Label ("Maximum queue size"));
queueLimitLabel = new Label("");
add (queueLimitLabel);
add (new Label ("Items processed"));
itemsProcessedLabel = new Label("");
add (itemsProcessedLabel);
add (new Label ("Time queue full"));
queueFullLabel = new Label("");
add (queueFullLabel);
add (new Label ("Time queue empty"));
queueEmptyLabel = new Label("");
add (queueEmptyLabel);
// Initialize the queue parameters
queueLimit = 10;
numberInQueue = 0;
queueEmpty = 0;
queueFull = 0;
// Set the labels in the display to indicate the current
// queue parameters
updateDisplay();
// Start the supplierand consumer running in separate threads.
// From this point on the supplier and consumer run
// independently of this applet
supplier.start();
consumer.start();
/// QUESTION: objects of three classes are now executing independently/
/// how could we modify the program (adding only about four lines) to
/// simulate the situation where people are removed from the queue
/// two at a time, rather than one at a time? In other words, I want
/// to represent the situation where one queue is processed by
/// two different operators
}
/*==============================================================================
stop
Called by the browser when this applet should be stopped. This
operation simply calls `requestStop' in the supplier and consumer. These
will then stop at the next convenient point
==============================================================================*/
public void stop()
{
consumer.requestStop();
supplier.requestStop();
}
/*==============================================================================
updateDisplay
Displays the values of the queue parameters in the applet. This is called
whenever an item is added or removed. Although it is not particularly efficient
to update all the displays every time any attribute changes, it doesn't cause
any problems in this simple program
==============================================================================*/
void updateDisplay()
{
queueLimitLabel.setText(Integer.toString(queueLimit));
queueFullLabel.setText(Integer.toString(queueFull));
queueEmptyLabel.setText(Integer.toString(queueEmpty));
numberInQueueLabel.setText(Integer.toString(numberInQueue));
itemsProcessedLabel.setText(Integer.toString(itemsProcessed));
}
/*==============================================================================
supply
This operation is called by the supplier object to simulate a person joining
the queue. If the queue is not full, that person is accepted onto the queue,
increasing its size by one. If the queue is full, its size does not increase,
and the attribute which keeps a count of the number of failed queue-joining
attempts is imcremented
==============================================================================*/
public void supply()
{
if (numberInQueue >= queueLimit)
queueFull++;
else
numberInQueue++;
updateDisplay();
}
/*==============================================================================
consume
Called by the consumer object to indicate that it is ready to deal with the
next person in the queue. If the queue is empty, then its stays empty, and
we record that the queue was empty when the consumer was idle
==============================================================================*/
public void consume()
{
if (numberInQueue == 0)
queueEmpty++;
else
{
itemsProcessed++;
numberInQueue--;
}
updateDisplay();
}
}
/*==============================================================================
QueueThread class
This is the base class for the supplier and consumer classes. It provides
functionality that is common to both the supplier and the consumer, including
the ability to wait for a random lenght of time.
Because this class is a sub-class of Thread, it can be executed as a separate
thread of execution, and therefore so can its sub-classes
==============================================================================*/
/// QUESTION: why is this class `abstract'?
abstract class QueueThread extends Thread
{
// keepGoing is set to `true' initially, and is set to false when the
// object should stop executing
boolean keepGoing = true;
// Queue is the queue object with which this supplier or consumer is
// associated
Queue queue;
// requestStop is called when it is time for this object to stop executing,
// e.g., when the applet is closed
/// QUESTION: this operation sets the value of `keepGoing', but no
/// operation in this class reads the value. What reads the value of
/// keepGoing?
public void requestStop()
{
keepGoing = false;
}
// setQueue sets the attribute that indicates the queue with which this object
// is associated
public void setQueue (Queue _queue)
{
queue = _queue;
}
// waitRandomTime waits for a specified random time between zero
// and `milliseconds'
public void waitRandomTime(int milliseconds)
{
int delay = (int)(Math.random() * milliseconds);
try {sleep(delay);} catch(Exception e){}
}
}
/*==============================================================================
Producer and Consumer classes
These are very simple in this example; they have only one operation `run'.
Run is called when the applet calls `consumer.start()' or `supplier.start()'
These operation continue to supply or consume people from the queue until
`keepGoing' is set to false. Changing the values of `waitRandomTime' will
change the simulated speed of arrivals or departures from the queue
==============================================================================*/
class Consumer extends QueueThread
{
public void run()
{
while (keepGoing)
{
/// QUESTION: how can this object be modified to indicate
/// that people leave the queue exactly twice as fast?
waitRandomTime(1000);
queue.consume();
}
}
}
class Supplier extends QueueThread
{
public void run()
{
while (keepGoing)
{
waitRandomTime(800);
queue.supply();
}
}
}
|