Case Study IA2 OOC
Case Study IA2 OOC
DEPARTMENT OF CSE
One example of an application for this model would be to control the display of data that is read
by your browser. As information arrives from the Internet, it is written to a buffer by the producer
thread. A separate consumer thread reads information from the buffer and displays it in your
browser window. Obviously, the two threads must be carefully synchronized.
Problem Statement
To illustrate how to address the sorts of problems that can arise when you try to synchronize
threads, let’s consider a simple application in which several threads use a shared resource. You’re
familiar with those take-a-number devices that are used in bakeries to manage a waiting line.
Customers take a number when they arrive, and the clerk announces who’s next by looking at the
device. As customers are called, the clerk increments the “next customer” counter by one.
Our task is to build a multithreaded simulation that uses a model of a take-a-number device to
coordinate the behavior of customers and a (single) clerk in a bakery waiting line. To help illustrate
the various issues involved in trying to coordinate threads, we will develop more than one version
of the program.
Problem Decomposition
Clerk—uses the TakeANumber to determine the next customer and will serve the customer.
Customer—represents the customers who will use the TakeANumber to take their place in
line.
The TakeANumber class must track two things: Which customer will be served next, and which
waiting number the next customer will be given. This suggests that it should have at least two
public methods: nextNumber(), which will be used by customers to get their waiting numbers,
and nextCustomer(), which will be used by the clerk to determine who should be served (Fig.
14.18). Each of these methods will simply retrieve the values of the instance
variables, next and serving, which keep track of these two values. As part of the object’s state,
these variables should be private.
How should we make this TakeANumber object accessible to all of the other objects—that is, to
all of the customers and to the clerk? The easiest way to do that is to have the main program pass
a reference to the TakeANumber when it constructs the Customers and the Clerk. They can each
store the reference as an instance variable. In this way, all the objects in the simulation can share
a TakeANumber object as a common resource. Our design considerations lead to the definition of
the TakeANumber class shown in Figure [fig-takeanumber1].
class TakeANumber {
private int next = 0; // Next place in line
private int serving = 0; // Next customer to serve
} // TakeANumber
Note that the nextNumber() method is declared synchronized. As we will discuss in more detail,
this ensures that only one customer at a time can take a number. Once a thread begins executing
a synchronized method, no other thread can execute that method until the first thread finishes.
This is important because, otherwise, several Customers could call the nextNumber method at
the same time. It’s important that the customer threads have access only one at a time, also
called mutually exclusive access to the TakeANumber object. This form of mutual exclusion is
important for the correctness of the simulation.
from another, let’s give each customer a unique ID number starting at 10001, which will be set in
the constructor method. Also, as we noted earlier, each Customer needs a reference to
the TakeANumber object, which is passed as a constructor parameter . Note that before taking a
number the customer sleeps for a random interval of up to 1,000 milliseconds. This will
introduce a bit of randomness into the simulation.
The Clerk thread should simulate the behavior of serving the next customer in line, so
the Clerk thread will repeatedly access TakeANumber.nextCustomer() and then serve that
customer. For the sake of this simulation, we’ll just print a message to indicate which customer is
being served. Because there’s only one clerk in this simulation, the only variable in its internal
state will be a reference to the TakeANumber object (Fig. 14.22). In addition to the constructor,
all we really need to define for this class is the run() method. This
leads to the definition of Clerk shown in Figure [fig-clerk1]. In this case, the sleep() method is
necessary to allow the Customer threads to run. The Clerk will sit in an infinite loop serving the
next customer on each iteration.
Finally, Bakery is the simplest class to design. It contains the main() method, which gets the
whole simulation started. As we said, its role will be to create one Clerk thread and
several Customer threads, and get them all started (Fig. [fig-bakery]). Notice that the Customers
and the Clerk are each passed a reference to the shared TakeANumber gadget.
-2pc
Now that we have designed and implemented the classes, let’s run several experiments to test
that everything works as intended. Except for the synchronized nextNumber() method, we’ve
made little attempt to make sure that the Customer and Clerk threads will work together
cooperatively, without violating the real-world constraints that should be satisfied by the
simulation. If we run the simulation as it is presently coded, it will generate five customers and
the clerk will serve all of them. But we get something like the following output: