Concurrent Programming (COMP 150-CCP) Spring 2008 Dr. Richard S. Hall April 24 th, 2008 Write your name on each sheet of paper you hand in. Make sure that mobile phones are turned off. This is a closed-book exam -- no notes, books, or any other forms of help are allowed. If you have difficulty understanding a question, feel free to ask for clarifications. The exam is worth 100 points total. 1. Basic FSP modeling (8 p) The following LTS graph depicts the behavior of a student: Create a corresponding FSP process for the student s behavior. STUDENT=(attend_lecture->do_homework->TEST), TEST=(late->STUDENT take_exam->grade), GRADE=(pass->STUDENT fail->beg_for_points->{fail,pass->student). 2. Sharing and mutual exclusion modeling in FSP (12 p) When programming on a group project, it is helpful to use a configuration management (CM) system to ensure that two people cannot edit the same source code file at the same time. CM systems require that a user check_out a file before the user can modify it and then the user must check_in the file when done with it. Create an FSP specification that models a FILE and a USER. Using these definitions, create an FSP composition, called CM, that shares a single file with two users. USER=(check_out->modify->check_in->USER). FILE=(check_out->check_in->FILE). or FILE=(check_out->modify->check_in->FILE). CM=({a,b:USER {a,b::file).
3. Terminology and concepts (20 p) a) What is deadlock and what are the necessary conditions for it to occur? (5 p) b) How does ordered acquisition of resource types avoid deadlock? Specifically relate your answer to the necessary conditions for deadlock from above. (3 p) c) What is the difference between deadlock and livelock? (3 p) d) What is the difference between livelock and starvation? (3 p) e) How is a monitor similar to a Java class? How is it different? (3 p) f) Explain what the nested monitor problem is and why it is problematic? (3 p) a) Deadlock is when all threads in a set are waiting for something that only one of the other threads in the set can cause, so none can make progress. The necessary conditions for deadlock are: mutual exclusion of resources, no preemption of resources, incremental resource acquisition, and a circular wait pattern. b) It eliminates the possibility of a circular waiting pattern. c) In deadlock, no thread is making progress at all, since they are all waiting. In livelock, threads are making progress, but the progress isn't useful since they just keep repeating the same action(s). d) In livelock, threads get chosen for execution, but with starvation they are never given a chance to execute. e) Both a monitor and a class encapsulate state and define methods to access and modify the state, but monitors guarantee mutual exclusion while classes do not. f) If a call to a monitor method is nested inside of another monitor method, it is possible that the process making the nested monitor method call will wait on a condition in the inner monitor and still retain the lock of the outer monitor, thus blocking any other processes trying to access the outer monitor creating a deadlock situation.
4. Concurrent programming in Java (15) A student who has never taken my concurrent programming class created the following implementation of a bounded object stack: public class BoundedObjectStack { private final int MAXSIZE = 10; private ArrayList list = new ArrayList(); public synchronized Object pop() throws InterruptedException { if (list.size() == 0) wait(); notify(); return list.remove(0); public synchronized void push(object obj) throws InterruptedException { if (list.size() == MAXSIZE) wait(); list.add(0, obj); notify(); public void clear() { list.clear(); notify(); public int getsize() { return list.size(); The student then decided to create a type-safe bounded stack for Strings using composition rather than inheritance. The following code is the student s attempt to implement the string stack: public class BoundedStringStack { private BoundedObjectStack stack = new BoundedObjectStack(); public synchronized String pop() throws InterruptedException { return (String) stack.pop(); public synchronized void push(string s) throws InterruptedException { stack.push(s); Consider the complete implementation of both classes and the fact that there can be any number of threads executing the code. What concurrency-related programming errors did the student make? For each mistake you find, explain why it would cause a problem. 1) Should be using while-loops instead of if-statements when calling wait(). 2) Both clear() and getsize() should be synchronized since they access mutable state. 3) Should be using notifyall() since there could be any number of threads. 4) There is a nested monitor situation in the subclasses stack, since it is using the outer object's lock.
5. More concurrent programming in Java (15 p) Given the following class: class ReadWriteLock { private int readers = 0; // number of readers private boolean writer = false; // writer flag public synchronized void acquirereadlock() throws InterruptedException { while (writer) wait(); readers++; public synchronized void releasereadlock() { readers--; if (readers == 0) notifyall(); public synchronized void acquirewritelock() throws InterruptedException { while ((readers > 0) writer) wait(); writer = true; public synchronized void releasewritelock() { writer = false; notifyall(); a) In releasereadlock(), would notify() be sufficient? Why or not? (5 p) b) Why would we describe the implementation of this monitor as unfair? (5 p) c) Describe how you could make this a fair implementation. (5 p) a) Yes, notify would be sufficient because there can only ever be one writer; thus, when readers equals zero there are no reader threads and if there are any writer threads, only one can enter the monitor. b) It is unfair because writers may never gain access to the lock if there are a lot of reader threads. c) Introduce variables to keep track of waiting writers and a boolean flag that indicates whose turn it is; thus, we could modify the methods like this: public synchronized void acquirereadlock() throws InterruptedException { while (((waitingwriters > 0) && writerturn) writer) wait(); readers++; public synchronized void releasereadlock() throws InterruptedException { writerturn=true; if (readers == 0) notifyall(); public synchronized void acquirewritelock() throws InterruptedException { waitingwriters++; while ((readers > 0 writer) wait(); waitingwriters--; writer = true; public synchronized void releasewritelock() throws InterruptedException { writerturn=false; notifyall();
6. More concurrent programming in Java (30 p) Implement a ThreadPool class in Java for Runnables (i.e., an interface with a run() method) that has the following characteristics: 1. Uses an ArrayList<Runnable> for an unbounded internal task queue, 2. The maximum number of threads is fixed and is passed into the constructor, which should create all threads in advance, 3. Tasks can be added and executed immediately after thread pool construction (i.e., it does not need to be started), 4. Use first-in, first-out task scheduling, 5. Has an addtask(runnable r) method that adds a task to the queue, but throws an IllegalStateException if the thread pool has been stopped, 6. Has a stop() method that marks the thread pool as permanently stopped and blocks the caller until all existing tasks have completed. 7. After a thread pool has been stopped, thread pool threads should exit when no tasks remain. Use the following skeleton and fill in the missing methods: public class ThreadPool { private ArrayList<Runnable> queue = new ArrayList<Runnable>(); private boolean active = true; private final int MAX_THREADS; public ThreadPool(int max) { MAX_THREADS = max; for (int i = 0; i < MAX_THREADS; i++) { new Thread(new Runnable() { public void run() { threadmain(); ).start(); public void addtask(runnable r) { // TODO: IMPLEMENT THIS! public void stop() { // TODO: IMPLEMENT THIS! private void threadmain() { // TODO: IMPLEMENT THIS! (Hint: If you are not familiar with ArrayList, it is sufficient to know that it is not thread safe and has methods for getting its size (size()), adding an element to the end of the list (void add(object o)), removing an element (Object remove(int index)), and getting an element (Object get(int index)).) public class ThreadPool { private ArrayList<Runnable> queue = new ArrayList<Runnable>(); private boolean active = true; private final int MAX_THREADS; public ThreadPool(int max) { MAX_THREADS = max; for (int i = 0; i < MAX_THREADS; i++) {
new Thread(new Runnable() { public void run() { threadmain(); ).start(); public void addtask(runnable r) { synchronized (queue) { if (!active) { throw new IllegalStateException( Thread pool stopped ); queue.add(r); queue.notifyall(); public void stop() { synchronized (queue) { active = false; queue.notifyall(); while (!queue.isempty()) { try { queue.wait(); catch (InterruptedException ex) { private void threadmain() { Runnable r; while (true) { synchronized (queue) { r = null; while ((queue.size() == 0) && active) { try { queue.wait(); catch (InterruptedException ex) { if (queue.size() > 0) { r = queue.remove(0); queue.notifyall(); else if ((queue.size() == 0) &&!active) { return; if (r!= null) { try { r.run(); // Outside of sync block catch (Exception ex) { ex.printstacktrace();