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 finalSemaphore lock = new Semaphore(4, true);
private finalMap<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 voidputName(String string) {
if(insertedNameInMap(string)) {
lock.release();
}
}
public String getName() {
try {
lock.acquire();
returnretrieveNames();
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
protected synchronizedString retrieveNames() {
for (String nameString : names.keySet()) {
Boolean extractedName = names.get(nameString);
if(!extractedName.booleanValue()) {
names.put(nameString, true);
returnnameString;
}
}
return null;
}
protected synchronizedboolean insertedNameInMap(String name) {
for (String nameStr : names.keySet()) {
if(nameStr.equalsIgnoreCase(name)) {
names.put(nameStr, false);
returntrue;
}
}
return false;
}
}
class Worker extends Thread {
private final intcounter;
private finalCustomerNames 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);
+ " 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);
+ " by Thread " + counter);
}
}
/**
* @author gaurav
*
*/
public class SemaphoreDemonstrationTest {
public static voidmain(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 : 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