You are on page 1of 9

COP5255 Spring 2009 Midterm Exam

Name (print legibly) _______________________________________ UF ID Number ____________________________________________

Examination Rules

Do not start until told to do so. No electronic devices. Students who finish early may quietly turn in their papers and leave only up until 5 minutes before the end of the exam. After that time, you must stay in your seat until all the exams have been collected and you have been dismissed. Leaving your seat or talking during this time will be considered a violation of the honor code. All students must stop writing immediately when the end of the exam is announced. Continued writing after the end of the exam will be considered a violation of the honor code. Two pages of notes are allowed; otherwise, the examination is closed book. The exam will last for 1 hour and 55 minutes (1:55pm-3:50pm)

On my honor, I have neither given nor received unauthorized aid in this examination. I have also read all of the above rules. Signature______________________________________________ Hints: 1. Look over all the questions and observe their point values before you start. 2. Use your time wiselyanswer the questions you know first 3. Read the questions carefully.

4. (25-35 points) The code fragment on the next page is obtain during the design of and Exchanger object which allows two threads to swap elements. The first thread to arrive at an empty exchanger by executing the exchange method inserts its item in buff and then waits inside the method. The second thread to execute the exchange method gets the item currently in buff, replaces it with its own and returns the first item. After the second thread has replaced the item, the first thread reads buff and returns the item. At this point, the exchanger object is empty and can be used by another thread. If a thread arrives when a. (5 points) Fill in the blanks and initialize the variable to ensure that the invariant is satisfied b. (10 points) Give an implementation using explicit locks and condition variables. Your implementation should be consistent with your answer in part a. c. (10 points) A thread waiting in the exchanger might be interrupted in order to cancel it. Your solution should handle this properlyafter a thread has been cancelled, it should be possible for other threads to continue using the Exchanger object. Briefly explain what you have done. (Good comments are sufficient.) d. EXTRA CREDIT (10 points) Modify the implementation to replace n1, n2, and n1r with bounded variables. To get credit, you must indicate, by strengthening the invariant, etc. how the new variables relate to n1,n2, and n1r.

Exchanger n1 = number of times a first thread has arrived to the exchanger n2 = number of times a second thread has arrived to the exchanger and returned the item inserted by the first thread n1r = number of time the first thread has returned. Invariant: (n1=n2 /\ n2=n1r) //exchanger is empty \/ (n1 = n2+1 /\ n2 = n1r) //first thread has arrived and is waiting for second. Its item is in buff \/ (n1 = n2 /\ n2=n1r+1) //second thread has arrived and returned, first thread still in exchanger.
//Second threads item is in buff.

public class Exchanger<E> { private E buff; private int n1 = 0; private int n2 = 0; private int n1r = 0; public E exchange(E e) { < await ((n1=n2 /\ n2=n1r) \/ (n1 = n2+1 /\ n2 = n1r)) if ((n1=n2 /\ n2=n1r) ) { //first thread buff = e; n1++; } else { //second thread n2++; E tmp = buff; buff = e; return tmp; } > < //first thread waits for a second thread await ((n1 = n2 /\ n2=n1r+1)) n1r ++ return buff; > } }

import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; public class Exchanger<E> { private int n1=0; private int n2=0; private int n1r = 0; private E buff; ReentrantLock lock = new ReentrantLock(); Condition c1 = lock.newCondition(); Condition c2 = lock.newCondition(); public boolean inv() //this wasnt required { return (n1==n2 && n2==n1r) //no threads at exchanger || (n1 == n2+1 && n2 == n1r) //waiting for second // thread to return || (n1==n2 && n2 == n1r+1); //first thread still // in exchanger } public E exchange(E e) throws InterruptedException { lock.lock(); assert inv(); try{ //while (!((n1==n2 && n2==n1r) || // (n1 == n2+1 && n2 == n1r))) {c2.await();} while (n2 != n1r) {c2.await();} //if (n1 == n2+1 && n2 == n1r) if(n1!=n2) //second thread { E tmp = buff; buff = e; n2++; c1.signalAll(); return tmp; } //if control is here, this is the first thread assert inv(); n1++; buff=e; //while (!(n1 == n2 && n2==n1r+1)) while (n2 != n1r+1) { try {c1.await(); } catch(InterruptedException ie) { if (!(n1 == n2 && n2==n1r+1)) { n1--; }//this is the first thread, //restore the state throw new InterruptedException(); //rethrow exception } } n1r++; c2.signalAll(); return buff; } finally{ assert inv(); lock.unlock();} } }

Introduce new int variable state

(n1=n2 /\ n2=n1r) //exchanger is empty \/ (n1 = n2+1 /\ n2 = n1r) //first thread has arrived and is waiting for second. Its item is in buff \/ (n1 = n2 /\ n2=n1r+1) //second thread has arrived and returned, first thread still in exchanger.
//Second threads item is in buff. /\

/\ /\

(n1=n2 /\ n2=n1r) => state = 0 (n1 = n2+1 /\ n2 = n1r) => state = 1 (n1 = n2 /\ n2=n1r+1) => state = 2
public class Exchanger<E> { private int n1=0; private int n2=0; private int n1r = 0; private state = 0; private E buff; ReentrantLock lock = new ReentrantLock(); Condition c1 = lock.newCondition(); Condition c2 = lock.newCondition(); public E exchange(E e) throws InterruptedException { lock.lock(); assert inv(); try{ //while (!((n1==n2 && n2==n1r) || // (n1 == n2+1 && n2 == n1r))) {c2.await();} while (state==2) {c2.await();} //if (n1 == n2+1 && n2 == n1r) if(state==1) //second thread { E tmp = buff; buff = e; n2++; state = 2; c1.signalAll(); return tmp; } //if control is here, this is the first thread assert inv(); n1++; state = 1 buff=e; //while (!(n1 == n2 && n2==n1r+1)) while (state != 2) { try {c1.await(); } catch(InterruptedException ie) { if (state != 2) { n1--; state = 0}//this is the first thread, //restore the state throw new InterruptedException(); //rethrow exception } } n1r++; state = 0; c2.signalAll(); return buff; } finally{ assert inv(); lock.unlock();} }

1) (10 points) Determine whether or not the given program contains data races. If not, justify your claim. If so, give a scenario for how the races could happen. If it does have data races, fix the program to eliminate them.
class MidExample { int a; boolean b; Thread t0 = new Thread(){ public void run(){ a = 1; b = true; } }; Thread t1 = new Thread(){ public void run(){ if (b) System.out.println(a); } } public static void main(String[] args) throws InterruptedException { MidExample m = new MidExample(); m.t1.start(); m.t0.start(); m.t1.join(); m.t0.join(); System.out.println(m.x); } }

2) (10 points) Explain what it means for an object to be safely published. Discuss whether or not your exchanger implementation from question 1 could be used to safely publish an object.

An object is properly constructed if its this reference does not escape the constructor. An object is safely published if it is properly constructed and its reference is made visible to other threads without being involved in a data race. (It was not asked for in the question, but you should know that threads that access a safely published object can be sure that they see a completely initialized object) The exchanger can be used to safely publish properly constructed objects. For both the first arriving thread and the second arriving thread, the source thread acquires the lock, writes the item in buff, releases the lock, and the destination thread then acquires the lock, reads the item, releases the lock. (Note that performing a wait involves releasing and reacquiring the lock.) Thus there is always a path of hb edges from writing value in buff, releasing lock, acquiring lock reading value in buff. There are also hb-edges from the final reading of buff and the next write of buff by some other thread that is reusing the exchanger.

(30 points, 5 points each) Indicate whether the following statements are true or false. If false, briefly explain why. (No credit will be given for an answer of false without a valid explanation.) a) __________An int variable count is shared among several threads, where the only actions on count are to read its value, and to increment it. It suffices to mark count as volatile, no additional synchronization is needed.

b) __________ The boolean variable b, initially false, is shared between multiple threads. One specific thread sets its value to true, the other threads read the value. It suffices to mark b as volatile, no additional synchronization is needed.

c) __________ Polling is usually the best approach for canceling threads in a concurrent program because it gives the programmer the most control over the behavior of the program and allows cleanup operations to be performed.

d) _________ An important difference between the use of synchronized blocks java.util.concurrent lock utilities is the to behave differently when a blocked thread is interrupted.

e) _________ Although the recommended idiom for implementing <await (B) S> in Java is synchronized(obj){while (!B) obj.wait();} if you are sure that a thread another thread will only call notify() when B is true, you can optimize the code fragment by replacing the while with an if.

f) _________Optimistic concurrency control is most useful in situations with high contention because it reduces the amount of blocking.

You might also like