Prof. Pitts CS554AH2: Operating Systems Spring 2020
Programming Assignment 2
Project Delivery: You should submit three Java programs in a ZIP file through Canvas by the project due date. Do
not use packages in any of your programs submitted for this assignment.
Part 1: In the first program, you will add synchronization (using semaphores) to a program that reads and displays a
file. However, the file is read by some number of threads as indicated by the programs command line arguments.
I provide a starter program for you to work from for this part of the assignment. The program uses command line
arguments as so:
java FileThreads InputFile.txt
where InputFile.txt is the input file the threads read (it could be called anything, but I supply a file called InputFile.txt)
and
line argument, go the the Build menu and click Run Arguments. This will open a textfield above the edit area in which you
can type the command line arguments.
The main method verifies that the command line arguments exist and are valid (the file exists and can be opened, and
the number of threads is less than or equal to 15). The main method then creates a private log (as a StringBuilder class
object) for each thread, creates a runnable object (giving the private log and index as arguments to the constructor), and then
starts the thread.
The run method is shown on the next page. The input file is a Java FileReader object, which supports the read()
method. The read() returns an integer value that either represents the character read from the file, or is -1 to represent the
end of the file. The read() method also can throw an IOException, which is caught. After the call to the read() method,
Thread.sleep() is called to put in a random delay. Since the Thread.sleep() can throw an Interrupted Exception, this is also
caught. Note that in any of the situations for end the thread’s reading of the file (end of file, IOException, or
InterruptedException), the behavior is the same: make all threads stop.
After the Thread.sleep() method returns, the character that was read is added to the end of the main log (in the variable
mainLog) and to the end of the thread private log (in the variable myLog).
When this program is run as is (unless you run it with only one thread), you’ll note that the display of the main log is
nothing like the original file. In fact, you may find that the program occasionally throws an ArrayOutOfBoundsException
on the append to one of the logs. Your task for this part of the assignment is to identify the critical section of the run
method, define and initialize a binary semaphore, and use the semaphore acquire() and release() methods to protect
the critical section. Remember, when a thread acquires the semaphore, it must eventually release it for correct behavior, no
matter what path through the code is taken.
1/4
Prof. Pitts CS554AH2: Operating Systems Spring 2020
Part 2: The next part requires you to add a set of counting semaphores to the provided Java program to solve the Tea
Drinkers Problem. The program again requires command line arguments and is called as
java TeaParty
where
of threads.
The Tea Drinkers Problem: A group of n tea drinkers sit around a table to drink tea and talk. To drink tea, each tea
drinker requires a cup, a saucer, and a spoon and tea drinker cannot drink tea until the tea drinker has all three. There are
only n-1 cups, n-1 saucers, and n-1 spoons but any tea drinker can take any cup, saucer, or spoon as long as it is free. Each
tea drinker is represented by a runnable class with the run method shown on the next page.
2/4
1. public void run() {
2. // use to generate a sleep between readng from the input file
3. // and writing the character read
4. Random r = new Random(System.nanoTime());
5. // this loop holds the worker threads until the main thread has started
6. // them all
7. while(! keepgoing) /* Empty statement */ ;
8. // a count for the display statement each thread prints
9. // if the count display stops without the final results, there
10. // may be a problem in your semaphore code.
11. int count = 1;
12. // main loop
13. while(keepgoing) {
14. System.out.println(“Thread ” + me + ” read ” + count++); // count
display
15. int c; // to read the next character
16. try {
17. if((c = inputFile.read()) < 0) { // if read() returns -1
18. // thenend of file
19. keepgoing = false; // stop the threads
20. System.out.println("Thread " + me + " is finished<*>“);
21. return;
22. }
23. Thread.sleep((r.nextInt(5)+1)*10); // sleep
24. } catch(IOException ie) { // read can throw this
25. System.out.println(“Caught IOException in thread ” + me);
26. keepgoing = false;
27. return;
28. } catch(InterruptedException iex) { // sleep can throw this
29. System.out.println(“Caught InterruptedException from sleep()”);
30. keepgoing = false;
31. return;
32. }
33. mainLog.append((char) c); // add character to main log
34. myLog.append((char) c); // add character to private log
35. }
36. System.out.println(“Thread ” + me + ” is finished<*>“);
37. }
Prof. Pitts CS554AH2: Operating Systems Spring 2020
You will use this class as an inner class of a public class called TeaParty. This class will also define a set of
semaphores that are used to synchonize the tea drinker threads in their use of the cups, saucers, and spoons. The
main method of TeaParty will initialize the semaphore counts (you will use counting semaphores, one for each of
the three items the tea drinkers need) and then the main method will create and start n TeaDrinker threads (you
main method should get the value for n from the String array parameter of the main method.
Add code in the highlighted area to use the semaphores to synchronize the tea drinkers to use the n-1 sets of cup,
saucers, and spoons.
Submit your Java program TeaParty.java for this part of the project as part of your zip archive file.
The Java Semaphore class is defined in the java.util.concurrent package. The semaphore methods that you will
need to use are:
• Semaphore(int permits), the constructor. permits is the intial value for the semaphore count.
• void acquire() Acquires a permit from this semaphore, blocking until one is available, or the thread
is interrupted. acquire() throws the InterruptedException and must be used in a try-catch block.
• void release() Releases a permit, returning it to the semaphore. No exception is thrown by this
method.
3/4
1.
2. class TeaDrinker implements Runnable {
3. private int myID;
4. private int mySleepTimeNS;
5. public TeaDrinker(int id, int sleep) {
6. myID = id;
7. mySleepTimeNS = sleep;
8. }
9. private void sleep(int t) {
10. try {
11. Thread.sleep(t);
12. } catch (InterruptedException ie) {
13. System.out.println(“Sleep interrupted; should not happen”);
14. }
15. }
16. public void run() {
17. System.out.println(“Tea drinker ” + myID +
18. ” starting after sleep of ” + mySleepTimeNS + “ns”);
19. sleep(mySleepTimeNS);
20. while (true) { // run for ever
21. sleep(mySleepTimeNS);
22. System.out.println(“Tea drinker ” + myID +
23. ” getting cup, saucer, and spoon”);
24. // add code to get the cup, saucer, and spoon here
25. System.out.println(“Tea drinker ” + myID +
26. ” drinking tea for ” + mySleepTimeNS + “ns”);
27. sleep(mySleepTimeNS);
28. System.out.println(“Tea drinker ” + myID +
29. ” finished drinking tea, ” +
30. “releasing cup, saucer, and spoon”);
31. // add code to release the cup, saucer, and spoon here
32. System.out.println(“Tea drinker ” + myID +
33. ” will talk for ” + mySleepTimeNS + “ns”);
34. sleep(mySleepTimeNS);
35. }
36. }
37. }
Prof. Pitts CS554AH2: Operating Systems Spring 2020
Part 3: In this program, you’ll write five separate Runnable classes:
• one class, Reader, reads a line of text from System.in and places it in a shared String variable
• one class, CCounter, looks at the shared String and places the number of characters in a shared
character count variable
• one class, VCounter, looks at the shared String and places the number of vowels in the string in a
shared vowel count variable
• one class, DCounter, looks at the shared String and places the number of digits in the string in a share
digit count variable
• one class,
Displayer
, uses the shared String , character count, vowel count, and digit count variable to
display (using System.out.println) the original string, the total number of characters, the number of
vowels and digits in the string
The threads have a dependency graph that looks like this:
This graph means that CCounter, VCounter, adn DCounter cannot not begin work until Reader has put a new string in
the share String variable. Displayer cannot begin work until all three of CCounter, VCounter, and DCounter have placed
their respective counts in the three shared variables. An after it has read the first string to begin the sequence, Reader must
wait until Displayer has displayer the current string and counts before it reads the next string.
This is similar to the example we saw a few weeks ago in which we divided that calculation of the perimeter of a circle
into four threads.
The run methods of each of the Runnable classes should have a forever while loop and should use semaphores to
sequence the threads properly. Use the semaphore version of the circle circumference program as an example.
Write a public class called WorkFlow with the five Runnable classes above as inner classes. The main method of
WorkFlow should create and start each of the threads. You should then be able to type lines and see the proper display for
each line.
You can code each of the run methods to break out of the while loop if the string is QUIT, but be careful to release the
necessary semaphores in the break condition to allow the other threads to quit.
Your WorkFlow class and all of the inner classes should be in a file called WorkFlow.java.
4/4
Reader
CCounter
VCounter
DCounter
Displayer
3. Coat a large bowl with nonstick spray and transfer dough to bowl, turning to coat. Cover with
plastic wrap and let rise in a warm, draft-free place until doubled in size, about 1 hour.
4. If making rolls, lightly coat a 6-cup jumbo muffin pan with nonstick spray. Turn out dough
onto a floured surface and divide into 6 pieces. Divide each piece into 4 smaller pieces
(you should have 24 total). They don’t need to be exact; just eyeball it. Place 4 pieces of dough
side-by-side in each muffin cup.
If making loaves, lightly coat two 9- by 5-inch loaf pans with nonstick spray. Turn out dough
onto a floured surface and divide into 12 pieces. Nestle pieces side-by-side to create 2 rows
down length of each pan.
If making split-top buns, lightly coat two 9- by 13-inch baking dishes with nonstick spray.
Divide dough into 12 pieces and shape each into a 4-inch long log. Place 6 logs in a row down
length of each dish.
5. Let shaped dough rise in a warm, draft-free place until doubled in size (dough should be just
puffing over top of pan), about 1 hour.
6. Preheat oven to 375 degrees F. Beat remaining egg with 1 teaspoon. water in a small bowl to
blend. Brush top of dough with egg wash and sprinkle with sea salt, if desired. Bake, rotating
pan halfway through, until bread is deep golden brown, starting to pull away from the sides of
the pan, and is baked through, 25 to 35 minutes for rolls, 50 to 60 minutes for loaf, or 30 to 40
minutes for buns. If making buns, slice each bun down the middle deep enough to create a split-top.
Let milk bread cool slightly in pan on a wire rack before turning out; let cool completely.