220 likes | 465 Views
Semaphores, Locks and Monitors. By Samah Ibrahim And Dena Missak. Semaphore Definition. A semaphore is a synchronization construct that can be used to provide Mutual Exclusion and Conditional Synchronization.
E N D
Semaphores, Locks and Monitors By Samah Ibrahim And Dena Missak
Semaphore Definition • A semaphore is a synchronization construct that can be used to provide Mutual Exclusion and Conditional Synchronization. • Mutual exclusion means that only one thread can have control over a shared resource at a certain point in time • Conditional synchronization :refer to synchronization using condition variables . • Two operations, Wait and Signal, are defined on condition variables. • Wait: suspends/blocks execution of the calling process if a certain condition is true • Signal: wake up a waiter, if any. • It is a shared object that can be manipulated only by two atomic operations, P and V. • It is a non-negative integer variable
Counting & Binary Semaphores • There are two types of semaphores: • Counting Semaphore • Binary Semaphore. • Counting Semaphore can be used for mutual exclusion and conditional synchronization. • Binary Semaphore is specially designed for mutual exclusion.
Counting Semaphore • A counting semaphore can be considered as a pool of permits. • A thread uses P operation to request a permit. If the pool is empty, • the thread waits until a permit becomes available. • A thread uses V operation to return a permit to the pool.
An Object Oriented Definition • A counting semaphore can be defined in an object-oriented manner as shown below: class CountingSemaphore { private int permits; public CoutingSemaphore (int initialPermits) { permits = initialPermits; } public void P() { ... } public void V() { ... } }
An Implementation Sketch • Here is a sketch of one possible implementation of methods P() and V(): Semaphore -> P() If permits >0,then decrement permits by 1 otherwise "wait" until permits>0 and then decrement Semaphore -> V() Increment permits by 1 Wake up a thread waiting in P() • No thread that is blocked on a P() operation remains blocked if the V() operation on the semaphore is invoked infinitely often • In practice, FIFO is mostly used, transforming the set into a queue.
Binary Semaphore • A binary semaphore must be initialized with 1 or 0, • and the completion of P and V operations must alternate. Why must P and V alternate?
Answer • if a binary semaphore is initialized with 1, and a certain thread obtained that permit by calling P() and then another thread calls P() again,that thread will be blocked unless V() is called.
If the semaphore is initialized with 1, then the first completed operation must be P. If the semaphore is initialized with 0, then the first completed operation must be V. • P operation can be blocked, if it attempted in a consecutive manner.
Counting vs. Binary Semaphore • Counting Semaphore • Can take any initial value • V operation never blocks • Completed P and V operations do not have to alternate • V could always be the first completed operation • Binary Semaphore • Can only take 0 or 1 • Both P and V operation may block • Completed P and V operations must alternate • If the initial value is 0, the first completed operation must be V; if the initial value is 1, the first completed operation must be P.
Locks • Lock provide mutual exclustion • A lock defines two types of operations: • Lock(): wait until lock is free, then grab it • Unlock(): release the lock, waking up a waiter,if any
Lock Ownership • A lock can be owned by at most one thread at any given time. • A thread that calls lock becomes the owner of a lock if the lock is not owned by any other thread; otherwise, the thread is blocked. • The owner of a lock can release the ownership by calling unlock. • Important: The owner of a lock is not blocked if it calls lock again. • However, the owner must call unlock the same number of times to release the ownership.
Binary Semaphore vs. Lock • Binary Semaphore • Has no concept of ownership • Any thread can invoke P or V operations • Consecutive P operations will be blocked • Need to specify an initial value • Lock • A lock can be owned by at most one thread at any given time • Only the owner can invoke unlock operations • The owner can invoke lock (or unlock) operations in a row. • Does not have to be initialized
Lock vs. Binary Semaphore Lock L = new Lock (); BinarySemaphore s = new BinarySemaphore (1); T1: T2: L.lock(); s.P(); L.lock(); s.P(); T1: T1: L.lock(); s.P(); T2: T2: L.unlock(); s.V();
Abstract Definition • Java does not provide any semaphore classes. We can however simulate semaphores in Java. public abstract class Semaphore { protected int permits; protected abstract void P() ; protected abstract void V() ; protected Semaphore (int initialPermits) { permits = initialPermits; } }
CountingSemaphore public final class CountingSemaphore extends Semaphore { public CoutingSemaphore (int initialPermits) { super(initialPermits;) } synchronized public void P () { if (permits >0) permits --; else { try { wait (); } catch (InterruptedException ex) {}; permits --; } } synchronized public void V () { permits++; if (permits <=1) { //it means the permits was zero notify (); } } } // wake up the threads if any
BinarySemaphore public final class BinarySemaphore extends Semaphore { public BinarySemaphore (int initialPermits) { super(initialPermits); if (initialPermits != 0 || initialPermits != 1) { throw new IllegalArgumentException( initial value must be 0 or 1. ); } } synchronized public void P () { while (permits == 0) { //no permit, queue the thread try { wait (); } catch (InterruptedException ex) {}; } permits = 0; //a thread will obtain a permit notifyAll (); } // notify the other threads that permits=0 synchronized public void V () { while (permits == 1) { //wait until the permit=0, then reset it try { wait (); } catch (InterruptedException ex) {}; } permits = 1; notifyAll (); } } //notify the threads that a permit exist
Lock Implemetation public final class Lock { private Thread owner = null; private int waiting = 0; //to count how many threads are waiting public int count = 0; //to count how many times the object is locked public boolean free = true; //means the lock is free public synchronized void lock () { if (free) { count = 1; free = false; owner = Thread.currentThread ();} else if (owner == Thread.currentThread()) { ++ count; } else { ++ waiting; try { wait(); } catch (InterruptedException ex) {} count = 1; owner = Thread.currentThread ()}}
Lock Implemetation continued public synchronized void unlock () { if (owner != null) { if(owner == Thread.currentThread ()) { -- count; if(count == 0) { owner = null; if (waiting > 0) { -- waiting; notify();} else { free = true; return; } }else return;} }throw new OwnerException ();}}
What are monitors? • Monitors provide a structured concurrent programming primitive, which is used by processes to ensure exclusive access to resources, and for synchronizing an communicating among users. • To synchronize tasks within the monitor, a condition variable is used to delay processes executing in a monitor. • Two operations, Wait and Signal, are defined on condition variables. • Wait: suspends/blocks execution of the calling process if a certain condition is true • Signal: wake up a waiter, if any. T1 T2 Wait Signal