Sunday, April 21, 2013

Semaphore in Java

Let's start with below problems; before starting on Semaphore.
  1. Implement a Database connection pool which will block if no more connection is available instead of failing and handover connection as soon as it's available
  2. Implement a thread pool
  3. Create a blocking bounded collection
  4. Limiting number of http connection to an external site
Note : Bounded puts an upper limit on the size of collection. 
And block(ing) here means that, wait for the collection to become non-empty when retrieving an element, and wait for space to become available when storing an element. 
(One of the) Solution to all above problems is, Semaphore!
Recall that, in normal monitor lock (implicit lock), only one thread can access a resource. So, if you want more than one thread to access a resource at the same time, Semaphore comes into play. Semaphores are used to restrict number of threads which can access a resource or perform a given action at the same time. To achieve this, semaphore maintains a counter which keeps track of the number of resources available.

When a thread requests access to resource, semaphore checks the variable count, and if it's less than total count, then grants access and subsequently reduces the available count. If count is equal to maximum allowed count then it asks thread to wait. If resource count is one (0/1 or on/off) then its called as Binary Semaphore, otherwise its called as counting semaphore.

Semaphore got introduced into standard Java library in version 5.

Limit Number of Http Connections

Below example show how can you limit the number of http connection in your system.

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.concurrent.Semaphore;

/**
 * Limit Maximum number of URL connection allowed through Counting Semaphore
 * @author Sid
 *
 */
class UrlConnectionManager {
 private final Semaphore semaphore;
 private final int DEFAULT_ALLOWEED = 10;

 UrlConnectionManager(int maxConcurrentRequests) {
  semaphore = new Semaphore(maxConcurrentRequests);
 }
 
 UrlConnectionManager() {
  semaphore = new Semaphore(DEFAULT_ALLOWEED);
 }

 public URLConnection acquire(URL url) throws InterruptedException,
   IOException {
  semaphore.acquire();
  return url.openConnection();

 }

 public void release(URLConnection conn) {
  try {
   // clean up activity if required
  } finally {
   semaphore.release();
  }
 }
}

In above example; semphore maintains a set of permits or pass. Method, semaphore.acquire(), blocks if necessary until permit is available and then takes it. And each semaphore.release() adds/returns a permit. Semaphore is like a gatekeeper which keeps track of number of visitors allowed in a building.

Blocking Bounded LinkedList

You can also use semaphore to turn any collection into blocking bounded collection. Let's see how to do the same for LinkedList. Assume that, only add and remove operations are allowed. Below class provides main method to test it. Note that, at most 5 add operations are allowed.

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * Minimilistic Bounded List  
 * @author Sid
 *
 */
public class BoundedLinkedList<T> {
 private List<T> list;
 private Semaphore semaphore;

 public BoundedLinkedList(final int bound) {
  list = Collections.synchronizedList(new LinkedList<T>());
  semaphore = new Semaphore(bound);
 }

 public void add(T o) throws InterruptedException {
  semaphore.acquire();
  list.add(o);
  System.out.println(" Added element :"+ o);
 }
 
 public void remove(T o) {
  if (list.contains(o)) {
   list.remove(o);
   semaphore.release();
   System.out.println(" Removed element :"+ o);
  }
 }
 
 /**
  * Test Method
  * Adds 6 elements; then waits for 5 seconds; removes one element
  */
 public static void main(String[] args){
     final BoundedLinkedList<Integer> bll = new BoundedLinkedList<>(5);
  new Thread(new Runnable(){
   public void run(){
    for(int i = 1; i <= 6; i++)
     try {
      bll.add(i);
     } catch (InterruptedException e) {
      e.printStackTrace();
     }
   }
  }).start();
  
  try {
   TimeUnit.SECONDS.sleep(5);
  } catch (InterruptedException e) {
   e.printStackTrace();
  } 
  
  bll.remove(3);
 }

}

Output
 Added element :1
 Added element :2
 Added element :3
 Added element :4
 Added element :5
 Removed element :3
 Added element :6


Important points to remember

  1. You should be very careful to make sure that you are releasing after acquire. You can miss it due to programming error or any exception. 
  2. Long held lock can cause starvation 
  3. Method, release doesn't have to be called by the same thread which called acquire. This is an important property that we don't have with normal mutex in Java.
  4. You can increase number of permits at runtime (you should be careful though). This is because number of permits in a semaphore is not fixed, and call to release will always increase the number of permits, even if no corresponding acquire call was made. 

---
keep coding !!!

No comments:

Post a Comment