1

From the theory i read there are two types

1)Binary :- 0 or 1 as value or in other words only one thread allowed to inside critical section(or maybe i didn't interpret it correctly)

2)Counting :- Allows only k out of n threads to access k resources [k= no of resources , n=no of threads always n >k]

  1. Why Binary Semaphore ?

If Binary Semaphore's i.e

Semaphore sem = new Semaphore(1); 

are used where only one thead is allowed inside don't they do the same thing as any normal LOCK? i.e only one thread allowed inside then how is

sem.aquire(); //since count=1 only one thread enters
try
{
 //Important Stuff which can be accessed only by one thread
}
finally{sem.release();}

any diffrent from this?

lock.lock()
try
{
 //Again only one thread allowed here same as binary semaphore
}
finally{lock.unlock();}
  1. Why Counting Semaphore ?

Now let's say there are

a) 3 Resources R1,R2,R3 and an resource can be used by only 1 thread at a time

b) 9 Threads T1,T2.....T9

c) Here is how each threads uses these resources

-> T1,T2,T3 => Wants R1

-> T4,T5,T6 => Wants R2

-> T5,T6,T7 => Wants R3

One would say counting semaphore would allow only 3 threads to aquire max 3 resources which would work fine provided each thread tries to aquire an unique resource but clearly not the case in this example. Lets say if T1,T2,T3 all entered the critical section T1 gets R1 and R1 is now removed from the list of available resources Now T2,T3 will crash because R1 is unavailable now.

You could put an lock on every method of the resource and not remove it from the resource queue to ensure if an resource is shared only one thread gets to use it but not only does that defeat the purpose of an semaphore but now we have overhead of both the semaphore and the lock.

How does an semaphore solve the problem of N Threads accessing 1 Resource i.e N->k where k is an resource in an queue of k1,k2,k3...kn resources?

My solution

Not perfect but it does not require locks and is fair

private final class Resource
 {
  
  private static final HashMap<Integer,Resource> instances=new HashMap(); //An map keeping track of available resources
  private static final Object aquireLock=new Object(); //allow only one thread to aquire an resource and add itself to the waiting queue if it was not available at a time
  private static final ArrayList<QueueObject> waiting=new ArrayList();//list of threads waiting to aquire an resource
  
  private static void init()  //Only 3 resources
  {   
   instances.put(0,new Resource());
   instances.put(1,new Resource());
   instances.put(2,new Resource());
  }
  
  private static void use(int index,Consumer<Resource> action)
  {
   Resource res=null;
   
   boolean doWait=false;
   synchronized(aquireLock)
   {
    if(instances.containsKey(index)){res=instances.remove(index);}//remove resource to make it unavailable
    else{doWait=true;}//not available? then wait
   }
   if(doWait)
   {
    QueueObject wait=new QueueObject(index); //store the name[index] of the resource it was asking so it can be woken up when that resource is available
    
    synchronized(waiting){waiting.add(wait);} //now wait in queue
    
    wait.doWait();
    
    waiting.remove(wait);//wait over remove itself from queue
   }
   if(res==null){res=instances.remove(index);}//after wait again try to get resource[if it already has resource res wont be null]
   
   try{action.accept(res);}//do stuff with resource
   finally
   {
    synchronized(aquireLock){instances.put(index,res);}//Resource utilized now return it back to the queue
     
    synchronized(waiting)
    {  
     waiting.stream()
            .filter(obj->obj.resID==index) //find the 1st thread in queue that was asking for this resource and wake it up so it can use it now
            .findFirst()
            .ifPresent(QueueObject::doNotify);
    }
   }
  }
  
  private static final class QueueObject
  {
   private boolean isNotified = false;
   private final int resID;
   
   private QueueObject(int resID){this.resID=resID;}
   
   private synchronized void doWait() 
   {
    while(!isNotified) //protection against missed signals and spurious wake up's
    {
     try{this.wait();}
     catch(InterruptedException ex){}
    }
    this.isNotified = false;
   } 

   private synchronized void doNotify() 
   {
     this.isNotified = true;
     this.notify();
   }
  }
}

So with Binary semaphore's doing the same stuff an an lock and counting semaphores having the issue of protecting 1 resource[in an queue of k resources] from N threads what additional benifit does an semaphore serve?

Sync it
  • 1,180
  • 2
  • 11
  • 29
  • Also worth a read: http://greenteapress.com/semaphores/LittleBookOfSemaphores.pdf – Lucero Aug 28 '20 at 11:22
  • _"does not require locks"_ that is not true, by using `synchronized` you are using a lock indirectly – Ackdari Aug 28 '20 at 11:22
  • 1
    Letting the verbosity and other flaws of your code aside, you are basically re-implementing the counting semaphore. That doesn’t say anything about whether or when such a construct will be needed. However, I never encountered a scenario for allowing *n* threads to access something (with *n* ≠ 1) in real life. In other words, I never used the Java class `Semaphore` and most likely never will. – Holger Aug 28 '20 at 11:39
  • From what i understand from the linked answer semaphores are not used for resource protection but rather to limit resource creation/usage and thread signalling[coudn't find an code sample for that though . All the accepted answers seem to be mostly openiated and still seems to be debated till this day so i guess it was best for this question to be closed before it starts an another debate :) – Sync it Aug 28 '20 at 13:28
  • @Holger you are right about never needing to use semaphores in today's high spec systems and even if the need ever arised it seems the problem can be solved using locks and **Conditions** rather than semaphores – Sync it Aug 28 '20 at 13:30
  • 1
    Cutting down resource consumption sounds like a valid use case, but even then, it’s usually solved by using a thread pool with a maximum size in the first place, instead of creating more threads and stopping them at a semaphore afterwards. – Holger Aug 28 '20 at 15:10
  • @RVISHAL semaphores are commonly used to implement producer-consumer queues where the resource managed is queue space. Semaphores are one of the very few ways, (if not the only way), of communicating I/O completion from a driver interrupt handler where the resource managed is completed I/O operations. Just two very important examples... – Martin James Aug 30 '20 at 04:02
  • @Holger - I would not bet on that... – Martin James Aug 30 '20 at 04:04
  • 1
    @MartinJames when I use a `BlockingQueue` with a limited capacity, threads are already blocked when the capacity is exhausted. What would I gain from adding a `Semaphore` here? The other example is even less useful, you are not doing interrupts in Java. This question is not about the concept of a semaphore, but the actual Java class `Semaphore` that implements it literally. Further, your example looks pretty much like a typical `n = 1` whereas this question is about `n > 1`. – Holger Aug 31 '20 at 07:09

0 Answers0