




Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Material Type: Exam; Professor: Hicks; Class: PROG LANG TECH & PDGMS; Subject: Computer Science; University: University of Maryland; Term: Fall 2002;
Typology: Exams
1 / 8
This page cannot be seen from the preview
Don't miss anything!





Put your name and class account number on each page before starting the exam. Write your answers directly on the exam sheets, using the back of the page as necessary. I will not accept exams until I ask for them. If you finish early use the time to recheck your answers. Please be as quiet as possible. I will not take any questions during the exam. Errors on the exam will be posted on the board as they are discovered. If you feel an exam question assumes something that is not written, write it down on your exam sheet. Barring some unforeseen error on the exam, however, you shouldn’t need to do this at all, so be careful when making assumptions.
(a) (4 points) How do locks (when used correctly) prevent data races? by ensuring that only one thread can execute code that would modify shared state. (b) (4 points) In addition to preventing data races, what important feature do Java locks provide? They ensure that changes to shared data in one thread are made visible to other threads (c) (6 points) During their lifetime, threads can be in a number of possible states; name two of them. Two of created, runnable, blocked, sleeping, or terminated. (d) (6 points) A state-dependent action within a method requires the object to be in an acceptable state before the action can be performed. For example, to return a value, a queue’s dequeue method requires the queue to be nonempty. Explain two ways that a state-dependent method can respond when the object is not in an acceptable state. Two of:
1: class Ex3 { 2: public Log lookupLog(Map map, String name) { 3: Log log = (Log) map.get (name); 4: if (log == null) { 5: log = new LocalLog(100); 6: map.add(name,log); 7: } 8: return log; 9: } 10: } Even when map is synchronized, you can have conflicting writes and read/write. Ex. T1 calls anEx3.lookupLog. name is not in map. map.get returns null. new Log created. T1 gets swapped out. T2 calls anEx3.lookupLog with same name as T had. T2 completes after inserting a new log into map. T1 resumes and inserts its map, overwriting the one inserted by T2.
public class MutualExclusionLock { public synchronized void acquire(); public synchronized void release() throws BadReleaseException; public synchronized boolean attempt(); }
The idea is to implement your own kind of lock that is slightly different from Java’s built-in locks:
(a) To acquire a lock, the program would call acquire(). This will block until the thread can acquire the lock (i.e. until the thread currently holding it releases it). A thread can only hold a lock once. That is, if a thread calls acquire(), and then calls acquire() again, it will block. (b) To release a lock, the program would call release(); this method will throw BadReleaseExcep- tion if a thread other than the one holding the lock tries to release it. (c) To attempt to acquire a lock, the program would call attempt(). If the lock is held by another thread, then attempt() will immediately return false, otherwise it will acquire the lock and return true.
Recall that the method Thread.currentThread() returns the Thread identifier of the currently running thread. Answer:
public class MutualExclusionLock { private Thread holder = null;
/* Just like Java---block until acquiring the lock; unlike Java, the same thread cannot hold the lock more than once. */ public synchronized void acquire() { while (holder != null) { try { wait(); } catch (InterruptedException e) { } } holder = Thread.currentThread(); }
/* Just like Java---release held lock; throws an exception if the wrong thread tries to release the lock */ public synchronized void release() throws BadReleaseException { if (holder != Thread.currentThread()) throw new BadReleaseException(); holder = null; notifyAll(); }
/* Attempt to acquire the lock; return true if successful or false otherwise. Does not block. */ public synchronized boolean attempt() { if (holder == null) { holder = Thread.currentThread(); return true; } else return false; } }
(b) (18 points) Use the bounded thread pool model, in which an executor queues the job to execute, and one or more threads drain the queue to run the jobs. This is similar to the idea of EventThreads in project 4. Implement the PooledExecutor class and a PooledWorkerThread class, using the following Queue interface: public interface Queue { void enqueue(Object o) throws QueueFullException; Object dequeue() throws QueueEmptyException; int currentSize(); int maxSize(); } You can assume that all of the methods of an implementation of Queue will be synchronized methods. More importantly, notice that the queue will not block when consistency conditions are met, but rather will fail by throwing an exception. In particular, if you try to enqueue on a full queue, it will throw an exception; likewise if you try to dequeue from an empty queue, it will throw an exception. Your PooledExecutor class and PooledWorkerThread class will be responsible for handling queue failures and waiting until conditions are acceptable before proceeding. In particular, notice that neither PooledExecutor nor PooledWorkerThread throw QueueFullException or QueueEmptyException. As such, a call to execute() should block while the PooledExecutor’s queue is full, and a similar situation will occur for PooledWorkerThread when the queue is empty. Answer: public class PooledExecutor implements Executor {
private Queue queue;
PooledExecutor(Queue queue, int numPooledThreads) { this.queue = queue; for (int i = 0; i < numPooledThreads ; i++) { (new PooledWorkerThread (queue)).start(); } }
public void execute(Runnable command) { while (true) { synchronized(queue) { try { queue.enqueue(command); queue.notifyAll(); break; } catch (QueueFullException e) { try { queue.wait();} catch (InterruptedException e1) {} } } } } }
public class PooledWorkerThread extends Thread { private Queue queue;
public PooledWorkerThread(Queue queue) { this.queue = queue; }
public void run() { while (true) { synchronized (queue) { try { ((Runnable) queue.dequeue()).run(); queue.notifyAll(); } catch (QueueEmptyException e) { try { queue.wait(); } catch (InterruptedException e1) {} } } } } }