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);
+ " 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 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 : 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
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