Tuesday, 31 December 2013

Semaphore In Multithreading



A view on Semaphore


Question: - What is Semaphore?

A new class named Semaphore is introduced with jdk 1.5 and it belongs to the package java.util.concurrent. This can be used in multithreaded programming. It is having a set of permits for a resource which is required by a Thread. Semaphore can be treated as a counter which is having
specified number of pass or permits. In pursuance of access a shared resource, Current Executing Thread must acquire a permit. If permit is already taken by other thread than current thread has to wait until a permit is available due to release of permit from different thread. This concurrency utility is very helpful and more effective in order to implement Thread Pool, database Connection pool and producer consumer design pattern etc.

A Semaphore can be created with two initialization parameters or only single parameter(permits) which are as follows:
  • Number of permits: - The number of permits controls the number of resources that we want to manage using the semaphore.
  • Fairness: - As we are dealing with multiple threads, and the process switches CPU context between threads, so there is a concept of fairness.
As per the theory a thread could attempt to acquire a permit and blocked due to unavailability of resource. Then a second thread could attempt to acquire a permit immediately when it is returned, before the first thread has time to wake up and acquire it, and take that permit from the waiting thread. This process could theoretically repeat infinitely, leaving that first thread blocked forever. In this situation fairness policy works, if we tell the Semaphore to be fair,
it maintains the order that threads arrived and requested a permit and serves permits in that order.

After the creation of Semaphore object, we can manage permits through the acquire() and release() methods. The acquire() method obtains one or more permits from the Semaphore and release() method releases one or more permits.

Question: - Where to use Semaphore?

Suppose we want to implement better DB connection pool and if all connections are engaged and no more connection is available then instead of throwing an error it should wait for connection availability.

Important facts about Semaphore

Semaphore class allows many overloaded version of tryAquire() method which acquires a permit, if one is available and returns immediately, with the value true, reducing the number of available permits by one. If no permit is available then this method will return immediately with the value false.

Second very useful method of Semaphore is acquireUninterruptibly() which acquires a permit, if one is available and returns immediately, reducing the number of available permits by one. If no permit is available then the current thread becomes disabled for thread scheduling purposes and lies dormant until some other thread invokes the release() method for this semaphore and the current thread is next to be assigned a permit.

Below program will define the use of semaphore and their available methods
 

package com.gaurav.corejava.multithreading;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;

class CustomerNames {
            private final Semaphore lock = new Semaphore(4, true);
            private final Map<String, Boolean> names = new ConcurrentHashMap<String, Boolean>();

            public CustomerNames() {
                        names.put("Gaurav", false);
                        names.put("Kumar", false);
                        names.put("Aryan", false);
                        names.put("Shivam", false);
            }

            public void putName(String string) {
                        if (insertedNameInMap(string)) {
                                    lock.release();
                        }
            }

            public String getName() {
                        try {
                                    lock.acquire();
                                    return retrieveNames();
                        } catch (Exception e) {
                                    e.printStackTrace();
                        }
                        return null;
            }

            protected synchronized String retrieveNames() {
                        for (String nameString : names.keySet()) {
                                    Boolean extractedName = names.get(nameString);
                                    if (!extractedName.booleanValue()) {
                                                names.put(nameString, true);
                                                return nameString;
                                    }
                        }
                        return null;
            }

            protected synchronized boolean insertedNameInMap(String name) {
                        for (String nameStr : names.keySet()) {
                                    if (nameStr.equalsIgnoreCase(name)) {
                                                names.put(nameStr, false);
                                                return true;
                                    }
                        }
                        return false;
            }
}

class Worker extends Thread {
            private final int counter;
            private final CustomerNames custNames;

            public Worker(CustomerNames server, int number) {
                        this.custNames = server;
                        this.counter = number;
            }

            @Override
            public void run() {
                        String name = custNames.getName();
                        System.out.println("Acquired Customer Name is : " + name
                + " by Thread " + counter);
                        try {
                                    Thread.sleep(3000);
                        } catch (Exception e) {
                                    e.printStackTrace();
                        }
                        custNames.putName(name);
                        System.out.println("Released Customer Name is : " + name
                + " by Thread " + counter);
            }
}

/**
 * @author gaurav
 *
 */
public class SemaphoreDemonstrationTest {
            public static void main(String[] args) {
                        CustomerNames custNames = new CustomerNames();
                        for (int i = 0; i < 4; i++) {
                                    Worker worker = new Worker(custNames, i);
                                    worker.start();
                        }
            }
}

Description of the above program:-

The CustomerNames class manages a set of four resources which are customer names as Strings. It stores its name Strings in a map that maps the String with boolean denoting whether or not the name String is currently retrieved. It creates a Semaphore, named lock, that maintains 4 permits and is fair (preserves the order of each thread that requests a resource.) When we want to obtain a name from the CustomerNames class, we have to call the getName() method. This method will first acquires a permit for a name by calling the Semaphore's acquire() method and then calls the internal synchronized method retrieveNames() that finds an available name by iterating over the map.

When we are finished with all the name, then we have to return it back to the CustomerNames class by calling the putName() method. The putName() method releases the name, permits and then checks it back into the CustomerNames class.

The Worker class is a thread class that acquires a name from the CustomerNames class, sleeps for 3 seconds, and then returns the same name to CustomerNames.

Finally, the SemaphoreDemonstrationTest class creates 4 Worker threads that will try each attempt to acquire one of the four managed name Strings. Below is the output of this example.

Result:-

Acquired Customer Name is : Aryan by Thread 0
Acquired Customer Name is : Gaurav by Thread 1
Acquired Customer Name is : Shivam by Thread 2
Acquired Customer Name is : Kumar by Thread 3
Released Customer Name is : Aryan by Thread 0
Released Customer Name is : Shivam by Thread 2
Released Customer Name is : Gaurav by Thread 1
Released Customer Name is : Kumar by Thread 3

1 comment:

  1. Semaphore is one of the topics I tend to ignore as it is complex and a little hard for me to understand. Tips written here somehow gave me a whole new perspective on how to go about this topic so I may understand it pretty well. If not I may look again for some research papers for sale that may aid me better.

    ReplyDelete