CS554AH2 Operating Systems
Programming Assignment
Part 1: In this part of the project, 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. I suggest that you use Jgrasp to implement your project. The program uses command line arguments as so:
java FileThreads InputFile.txt <num-threads> where InputFile.txt is the input file the threads read (it could be called anything, but I supply a file called InputFile.txt)
and <num-threads> is the number of threads to start (must be less than 15). To run the program in Jgrasp with command 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. |
{`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. 37. }`} |
System.out.println("Thread " + me + " is finished<*>"); |
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 <num-threads> where <num-threads> is the number of threads to start. In the description of the problem that follows, n is the number
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.
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. |
{`}`} |
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.