Semaphore is one of the synchronization aid provided by java concurrency util in Java 5 along with other synchronization aids like CountDownLatch, CyclicBarrier, Phaser and Exchanger.
The Semaphore class present in java.util.concurrent package is a counting semaphore in which a semaphore, conceptually, maintains a set of permits. Semaphore class has two methods that make use of permits -
- acquire() - Acquires a permit from this semaphore, blocking until one is available, or the thread is interrupted. It has another overloaded version acquire(int permits).
- release() - Releases a permit, returning it to the semaphore. It has another overloaded methodrelease(int permits).
Here be informed that no actual permit objects are used; the Semaphore just keeps a count of the number available and acts accordingly thus the name Counting Semaphore.
How Semaphore works
Thread that wants to access the shared resource tries to acquire a permit using acquire() method. At that time if the Semaphore's count is greater than zero thread will acquire a permit and Semaphore's count will be decremented by one.
If Semaphore's count is zero, when thread calls acquire() method, then the thread will be blocked until a permit is available.
When thread is done with the shared resource access, it can call the release() method to release the permit. That results in the Semaphore's count incremented by one.
Semaphore class constructors
- Semaphore(int permits) - Creates a Semaphore with the given number of permits and nonfair fairness setting.
If a Semaphore is initialized with 5 permits that means atmost 5 threads can call the acquire method without Calling release method.
- Semaphore(int permits, boolean fair) - Creates a Semaphore with the given number of permits and the given fairness setting.
When fairness is set true, the semaphore guarantees that threads invoking any of the acquire methods are selected to obtain permits in the order in which their invocation of those methods was processed (first-in-first-out; FIFO).
Binary Semaphore
A semaphore initialized to one, and which is used such that it only has at most one permit available, can serve as a mutual exclusion lock. This is more commonly known as a binary semaphore, because it only has two states: one permit available, or zero permits available. When used in this way, the binary semaphore has the property (unlike many Lock implementations), that the "lock" can be released by a thread other than the owner (as semaphores have no notion of ownership). This can be useful in some specialized contexts, such as deadlock recovery.
Semaphore Usage
- Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.
- Semaphore can also be used to facilitate inter-thread communication like in producer-consumer kind of scenario.
Let's see one example where Semaphore is used to control shared access. Here we have a shared counter and three threads using the same shared counter and trying to increment and then again decrement the count. So every thread should first increment the count to 1 and then again decrement it to 0.
Code without Semaphore
public class SemaphoreDemo {
public static void main(String[] args) {
SharedCounter counter = new SharedCounter();
// Creating three threads
Thread t1 = new Thread(counter, "Thread-A");
Thread t2 = new Thread(counter, "Thread-B");
Thread t3 = new Thread(counter, "Thread-C");
t1.start();
t2.start();
t3.start();
}
}
class SharedCounter implements Runnable{
private int c = 0;
// incrementing the value
public void increment() {
try {
// used sleep for context switching
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
c++;
}
// decrementing the value
public void decrement() {
c--;
}
public int getValue() {
return c;
}
@Override
public void run() {
this.increment();
System.out.println("Value for Thread After increment - " + Thread.currentThread().getName() + "" + this.getValue());
this.decrement();
System.out.println("Value for Thread at last " + Thread.currentThread().getName() + "" + this.getValue());
}
}
Output
Value for Thread After increment Thread-A 1
Value for Thread at last Thread-A 1
Value for Thread After increment Thread-B 2
Value for Thread at last Thread-B 0
Value for Thread After increment Thread-C 1
Value for Thread at last Thread-C 0
Here it can be seen that output has not come right. Thread-B while incrementing is still using the value which was incremented by Thread-A.
Code with Semaphore
import java.util.concurrent.Semaphore;
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore s = new Semaphore(1);
SharedCounter counter = new SharedCounter(s);
// Creating three threads
Thread t1 = new Thread(counter, "Thread-A");
Thread t2 = new Thread(counter, "Thread-B");
Thread t3 = new Thread(counter, "Thread-C");
t1.start();
t2.start();
t3.start();
}
}
class SharedCounter implements Runnable{
private int c = 0;
private Semaphore s;
SharedCounter(Semaphore s){
this.s = s;
}
// incrementing the value
public void increment() {
try {
// used sleep for context switching
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
c++;
}
// decrementing the value
public void decrement() {
c--;
}
public int getValue() {
return c;
}
@Override
public void run() {
try {
// acquire method to get one permit
s.acquire();
this.increment();
System.out.println("Value for Thread After increment - " + Thread.currentThread().getName() + "" + this.getValue());
this.decrement();
System.out.println("Value for Thread at last " + Thread.currentThread().getName() + "" + this.getValue());
// releasing permit
s.release();
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Output
Value for Thread After increment - Thread-A 1
Value for Thread at last Thread-A 0
Value for Thread After increment - Thread-B 1
Value for Thread at last Thread-B 0
Value for Thread After increment - Thread-C 1
Value for Thread at last Thread-C 0
Now it can be seen that access is controlled using Semaphore. Semaphore acquires and releases a permit after it is done manipulating the shared resource.
Semaphore inter-thread communication example
Let's see another example where producer-consumer is implemented using semaphores. Idea is to have 2 semaphores when first is acquired release second, when second is acquired release first. That way shared resource has controlled access and there is inter-thread communication between the threads.
Remember unlike RentrantLock, Semaphore can be released by a thread other than the owner (as semaphores have no notion of ownership).
public class SemConProdDemo {
public static void main(String[] args) {
Shared s = new Shared();
// Producer and Consumer threads
Thread t1 = new Thread(new SemProducer(s), "Producer");
Thread t2 = new Thread(new SemConsumer(s), "Consumer");
t1.start();
t2.start();
}
}
// Shared class used by threads
class Shared{
int i;
// 2 semaphores
Semaphore sc = new Semaphore(0);
Semaphore sp = new Semaphore(1);
public void get(){
try {
// acquiring consumer semaphore
sc.acquire();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Got - " + i);
// releasing producer semaphore
sp.release();
}
public void put(int i){
try {
// acquiring producer semaphore
sp.acquire();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.i = i;
System.out.println("Putting - " + i);
// releasing consumer semaphore
sc.release();
}
}
// Producer thread
class SemProducer implements Runnable{
Shared s;
SemProducer(Shared s){
this.s = s;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
s.put(i);
}
}
}
// Consumer thread
class SemConsumer implements Runnable{
Shared s;
SemConsumer(Shared s){
this.s = s;
}
@Override
public void run() {
for(int i = 0; i < 5; i++){
s.get();
}
}
}
output
Putting - 0
Got - 0
Putting - 1
Got - 1
Putting - 2
Got - 2
Putting - 3
Got - 3
Putting - 4
Got - 4
Source : https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Semaphore.html
That's all for this topic Semaphore in Java. If you have any doubt or any suggestions to make please drop a comment. Thanks!
Related Topics
You may also like -