260 likes | 352 Views
Practice Session 8. Blocking queues Producers-Consumers pattern Semaphore Futures and Callables Advanced Thread Synchronization Methods CountDownLatch Thread cancellation: Stop shouldStop Interrupt. Blocking Queues. An ordinary queue with a special feature.
E N D
Practice Session 8 Blocking queues Producers-Consumers pattern Semaphore Futures and Callables Advanced Thread Synchronization Methods CountDownLatch Thread cancellation: Stop shouldStop Interrupt
Blocking Queues • An ordinary queue with a special feature. • The queue behaves differently in two cases: • Empty queue case • Thread wants to pop head element of the queue. • Thread is blocked until the queue stops being empty • Full queue case • Thread wants to add an element to the queue. • Thread is blocked until the queue stops being full
Java Blocking Queue • API: • Package java.util.concurrent • Interface: BlockingQueue<E> • “Our” Implementation: • class MyBlockingQueue<E> implements BlockingQueue<E> • Java’s Implementation: • Java.util.concurrent.ArrayBlockingQueue<E> • Functions: • void put(E o) • Adds the specified element to this queue, waiting if necessary for space to become available. • E take() • Retrieves and removes the head of this queue, waiting if no elements are present on this queue. • API Website: • http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html • http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/ArrayBlockingQueue.html
MyBlockingQueue • Private Fields: private ArrayList<E> fList; • The queue object. • Not synchronized. private final int fMax; • Our queue’s maximum size. • Received upon construction. • Fixed Size. • Constructor: MyBlockingQueue(int max){ fList = new ArrayList(); fMax = max; } • Private Functions: private synchronizedintgetSize(){ return fList.size(); }
My Blocking Queue – put() public synchronized void put(E obj){ while(getSize()>=fMax){ try{ this.wait(); } catch (InterruptedException ignored){} } fList.add(obj); // wakeup everybody. If someone is waiting in the get() // method, it can now perform the get. this.notifyAll(); }
MyBlockingQueue – take() public synchronized E take(){ while(size()==0){ try{ this.wait(); } catch (InterruptedException ignored){} } E obj= fList.get(0); fList.remove(0); // wakeup everybody. If someone is waiting in the add() // method, it can now perform the add. this.notifyAll(); return obj; }
The Producer-Consumer Problem • A classical multi-process (thread) synchronization problem. • Uses a bounded (fixed-size) queue. • Two major groups: • Producer(s): • A Thread that generates new objects • Adds them to shared space • Consumer(s): • A Thread that removes objects from shared space. • Uses them. • Full queue: • A producer thread is blocked, until a free space is available. • Empty queue: • A consumer thread is blocked, until the queue receives new object.
Producer-Consumer Implementation • Using ArrayBlockingQueue • Three Classes: • Producer implements Runnable • Consumer implements Runnable • Driver class (includes main function) • Code Example: • Producer-Consumer • The output of the program does not necessarily reflect its flow, since printing and accessing the queue are 2 separate operations (the operating system might decide to run the first command, and then stops the thread and runs another thread, before this thread performs the second command). • Surrounding the 2 commands with a synchronized block solves this problem, but it’s not advisable since it blocks too much.
java.util.concurrent.Semaphore • A data structure used to restrict access to shared resources. • A synchronized block allows access for one thread at a time. • A semaphore gives N>=1 permits to threads, at the same time, over a resource/the code that comes after semaphore.acquire(<number>). • If the program uses acquire(), without a number, then the semaphore gives access to N threads. API: • http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Semaphore.html
Semaphore construction: • Semaphore sem = new Semaphore(int permits): • permits holds the maximal number of possible threads to access an object at the same time. • Fairness is false in this case, permits are given in an arbitrary order. • Semaphore sem = new Semaphore(int permits, boolean fair) • Fairness is true, gives permits to threads in FIFO manner. • First thread requesting access, is given access. • Downside: • It takes more time for the virtual machine to order the acquisition of the permits than to allow an arbitrary thread to acquire a permit.
Some Semaphore Methods • sem.release(): releases a permit returning it to the semaphore. • sem.release(n): releases n permits - adds n permits to the semaphore (n can be larger than the value we used at initialization). • sem.reducePermits(n): shrinks the number of available permits by the indicated reduction; does not block like acquire(). (protected method) • sem.acquire(): acquire a permit • sem.acquire(n ): acquire n permits • More methods in the api: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/Semaphore.html
Uses • Limiting concurrent access to a resource, in general. • Limiting the number of created threads. • Limiting the number of concurrent connections. • Etc.
Code Example – In the Club • Groups of people asking for entrance permits to a club. Group1 requests n1 entrance permits FiftyCentTheBouncer Club 5 entrance permits Group2 requests n2 entrance permits Group3 requests n3 entrance permits requests n4 entrance permits Group4 requests n5 entrance permits Group5
Callable • java.util.concurrent.Callable: • Like Runnable, but: • Allows Threads to return values. • Allows Threads to throw exceptions. • Uses generics to define object types. • Class Header: public class <threadName> implementsCallable<returnObjectName> • Required Functions: • public <returnObjectType> call() • Same purpose as Runnable’srun()
Futures • Used in combination with the Executor. • An object that represents the result of a Callable. • Can retrieve the Callable’s result: • Using get() method • The result can be retrieved only when the computation has completed. • get() blocks, until value can be retrieved. • Allows cancellation of Thread execution. • Using cancel() method • Cannot cancel a completed Callable. • Interface: http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html
Code Example – The Price is Right • A game were contestants try to guess the price of an item, given a max and a min value • . The contestant whose guess is closest to the actual price wins. Contestant1 ThePriceIsRight Item: Xerox Phaser 5500DN Workgroup Laser Printer. Actual price: $500 makes a guess between $1 - $1000 Contestant2 makes a guess between $1 - $1000 Contestant3 makes a guess between $1 - $1000 makes a guess between $1 - $1000 Contestant4 makes a guess between $1 - $1000 Contestant5
Create n Callables and save their futures in a collection. • While not all n values were retrieved: • Acquire a semaphore (a semaphore is released at the end of each Callable) • Look for the Callable that finished, using the collection of futures. • Get the value returned by the callable that finished. • Set the found future to null in the collection, so it won’t be checked again.
Notes • Runnable, Callable are objects, they are not threads. • Threads receive these objects and run them. • Threads receive a reference to these objects. Threads do not create a new copy of the runnable or the callable objects. • Having a stateful runnable/callable object (changes its own values) and running more than one thread to it will cause problems if not synchronized! • Runnable objects cannot throw exceptions back to main. Run() method does not throw Exception • Callable objects can throw exceptions back to main. Call() method throws Exception.
Thread Cancellation • Thread t = new Thread(…); • How? • t.stop() method. (deprecated) • Good? (doesn’t leave the objects in a stable state) • Unsafe! • Releases all object locks instantly. • Then, how? • By implementing a “shouldStop” method. • “shouldStop” checks a flag in the thread. • Case where a flag is true, thread stops working. • Good? • Not always! • Thread might not take too long to stop: • In case where the thread in sleep condition. • Thread might not stop at all: • In case where the thread in wait() condition. And no notify() on the horizon.
shouldStop Example class Worker implements Runnable { private booleanfShouldStop ; public Worker() { fShouldStop=false; } public synchronized void stop() { fShouldStop = true; } public synchronized booleanshouldStop() { return fShouldStop; } public void run() { while (!this.shouldStop()){ doTheWork(); } System.out.println("stopping…"); } }
interrupt() • The interrupt() mechanism. • Each thread stores an internal flag known as interrupt status. • Methods: • t.isInterrupted() • Checks whether the thread is interrupted or not. • t.interrupt(): • If t is blocked (wait(), sleep()) then • InterruptedException is thrown. • Forces the thread to wake up! From sleep or wait mode. • Otherwise • isInterrupted() will return true. • Behaves the same as shouldStop. • Note: • IfInterruptedException is thrown, isInterrupted()will returnfalse unless interrupt()is called again.
interrupt() example class Worker implements Runnable { public Worker() { } public boolean condition() { return false; } public synchronized void doSomeWork() { while (!this.condition() && !Thread.currentThread().isInterrupted()) { try { this.wait(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); // re-raise the interrupt. This is very important! break; // get out of the loop (and out of the method) } } } public void run() { while (!Thread.currentThread().isInterrupted()){ doSomeWork(); } System.out.println("stopping ;)"); } }
interrupt() example continued • class Driver{ public static void main(String[] args) { Thread t = new Thread(new Worker()); t.start(); try { Thread.sleep(100); } catch (InterruptedException e) { } t.interrupt(); } }
java.util.concurrent.CountDownLatch • What? • A synchronization method. • Allows one or more threads to wait until other threads complete. • How? • A CountDownLatchobject is initialized with a starting value. • The await() method blocksuntil the current count reaches zero due to invocations of the countDown()method in other threads. • After which all waiting threads are released and any subsequent invocations of await return immediately. • Properties: • The CountDownLatchcannot be reset. • This is a goodpractice for initialization/finalizationpurposes. • When we need to use some waiting point only once, the latch is best to do the job.
Example: • In multiplayer games you don’t want any player to start until all players have joined. • This procedure works only once at the start of the game. • API: • http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/CountDownLatch.html • Code Example: server waiting for clients to finish, before it shuts down. • CountDownLatch