Semaphore in Java with Example

In Java, we usually implement semaphore in thread synchronization. This article will give you a clear insight into this concept. Let’s start!!!

What is semaphore in java?

When there are many threads waiting to access a shared resource, various issues occur in resource allocation. A semaphore makes this task a bit easier by limiting the number of threads that can access a shared resource. It is a non-negative variable that is shared among the thread is denoted as a counter.

  • If counter > 0, access to the resources is granted.
  • If counter = 0, access to the resources is denied.

It sends signals among the threads to maintain cooperation during this process. It helps in avoiding race conditions and synchronizes the processes effectively.

To put it in simple words, a semaphore can limit the maximum of 5 connections to access a file concurrently.

Features of Java semaphore:

  • It allows synchronization to take place among the threads.
  • It provides a low-level synchronization mechanism by decreasing the level of synchronization.
  • The value of a semaphore is either zero or greater than zero. It is never a negative value.
  • The implementation of a semaphore can be done using test operations and interrupts. To execute a semaphore, file descriptors are used.

How does a semaphore works in java?

It takes control over the shared resources and regulates them using a counter variable. We already knew that this counter variable could not be a negative value. It can be zero or greater than 0.

If the counter > 0, the thread will be granted permission to access the shared resource. In this situation, the counter value will be decremented by 1.

Else, the thread stays in the blocked state until it gains permission.

Once the thread uses the resource, it releases it. At this instance, the counter value gets incremented by 1.

The next thread that is waiting to access the resource will be granted permission.

When the counter = 0, the thread is denied of being granted permission to access the shared resource.

Working of java semaphore through a flowchart:

The above-explained points are demonstrated in a flow chart:

working of semaphore in java

Semaphore class in Java:

Java contains easy ways to implement the semaphore mechanisms. This is possible using the Semaphore class in it. This class is present in java.util.concurrent package.

As this class provides all the functionalities by default, you need not crack your heads thinking about what to implement there.

Constructors in Java Semaphore class:

The Semaphore class in Java contains two contractors:

1. Semaphore(int num)

2. Semaphore(int num, boolean how)

The value of num is the number of threads that have permission to access the shared resource. If the boolean value is set to true, the thread receives access to use the resource.

Methods present in Java Semaphore class:

The Semaphore class contains various built-in methods.

The popular methods in this class are:

  • acquire(): Once the permit is available, it returns true; otherwise, it returns false.
  • release(): It releases the permit.
  • available(): This method returns the count of available permits.

Types of Java Semaphores:

Semaphores are categorized into four types:

1. Counting Semaphores

2. Bounded Semaphores

3. Timed Semaphores

4. Binary Semaphores

1. Counting Semaphores in Java:

In situations where we require more than one process to execute simultaneously, the counting semaphores play a major role. It uses the take() method to achieve this.

Sample program for Counting Semaphore:

public class CoutingSemaphore{
private int signals = 0;
public synchronized void take(){
this.signals++;
this.notify();
}
public synchronized void release() throws InterruptedException{
while(this.signals == 0)
wait();
this.signals--;
}
}

2. Java Bounded semaphores:

There is no upper bound value stored in the counting semaphore. To know the count of signals it can store, we use the bounded semaphores to set the upper bound.

Sample Program for Bounded Semaphore:

public class BoundedSemaphore{
private int signal = 0;
private int bound = 0;
public BoundedSempahore(int upperBound){
this.bound = upperBound;
}
public void synchronized take() throws InterruptedException{
while(this.signal == bound)
wait();
this.signal++;
this.notify++;
}
public void synchronized release() throws InterruptedException{
while(this.signal == 0)
wait();
this.signal--;
}
}

3. Java Timed Semaphores:

While using this semaphore, we can make the thread run for a certain period of time. Once the duration gets over, the times reset, and permits get released.

4. Java Binary Semaphore:

The binary semaphores act like the counting semaphores. The only difference is that binary semaphores accept only binary values, either 0 or 1.

If the binary semaphore value = 1, the operation succeeds. If the value is 0, the operation does not take place.

Semaphores as Locks in Java:

In Java, using bounded semaphore as a lock can also take place. To achieve this, we must set the value of the upper bound to 1 and invoke the take() and release() methods. Using these methods, take control of the critical section.

Sample snippet to implement using semaphores as locks in java:

Ssemaphore sem = new Semaphore(1);
sem.take();
try{
//critical section
}
finally{
sem.release();            
}

Implementing Semaphore in Java:

import java.util.concurrent.*;
class FirstCode{
static int count = 0;
}
class FirstCode2 extends Thread{
Semaphore s;
String threadName;
public FirstCode2(Semaphore s, String threadName){
super(threadName);
this.s = s;
this.threadName = threadName;
}@Override
public void run(){
if(this.getName().equals(“Thread1”)){
try{
System.out.println(“Starting ”+ threadName);
try{
System.out.println(threadName + “is waiting for permit”);
s.acquire();
System.out.println(threadName + “gets a permit”);
for(int i = 0; i<5; i++){
SharedResource.count++;
System.out.println(threadName+ “:” + SharedResource.count);
Thread.sleep(10);
}
}
catch(InterruptedExecution exc){
System.out.println(exc);
}
System.out.println(threadName + “releases the permit”);
s.release();
}
else{
System.out.println(“Starting” +threadName);
try{
System.out.println(threadName + “gets a permit”);
for(int i=0; i<5; i++){
SharedResource.count--;
System.out.println(threadName+ “:” + SharedResource.count);
Thread.sleep(10);
}
}
catch(InterruptedException exc){
System.out.println(exc);
}
System.out.println(threadName + “releases the permit”);
s.release();
}
}
}
public class SemaphorePgm{
public static void main(String args[]) throwsInterruptedException{
Semaphore s = new Semaphore(1);
FirstCode2 fc1 = new FirstCode2(s, “Thread1”);
FirstCode2 fc2 = new FirstCode2(s, “Thread2”);
fc1.start();
fc2.start();
System.out.println(“count: ”+ SharedResource.count);
}
}                                               

Output:

Starting Thread1
Starting Thread2
Thread1 is waiting for a permit.
Thread2 is waiting for a permit.
Thread1 gets a permit.
Thread1: 1
Thread1: 2
Thread1: 3
Thread1: 4
Thread1: 5
Thread1 releases the permit.
Thread2 gets a permit.
Thread2: 4
Thread2: 3
Thread2: 2
Thread2: 1
Thread2: 0
Thread2 releases the permit.
count: 0

Explanation of the program:

This program takes the help of a semaphore to control the access of the count variable, which is a static variable within the Shared class. The Shared.count increments five times by thread fc1 and decreases five times by thread fc2. Now we must prevent these two threads from accessing the Shared.count. to achieve this, access is allowed only after gaining the permit from the controlling semaphore. In this way, only one thread at a time will access the Shared.count as provided in the output.

Note that the call to sleep() inside the run() method is within the FirstCode2 class. it proves the access to Shared.count is synchronized by the semaphore. In the run() method, the class to sleep() method enables the thread to pause between each access to Shared.count. Generally, this enables the second thread to run.

Yet, due to the presence of semaphore, this second thread must wait until the first thread releases its permit. This takes place only if the first thread completes all its tasks. Therefore, the Shared.count is incremented five times by thread fc1 and decremented fives times by thread fc2. these increments and decrements are not intermixed at the assembly code.

In the absence of semaphore, the accesses to Shared.count by both threads would have occurred simultaneously. Also, the increments and decrements would have been intermixed. To validate this, you will notice that the access to Shared.count is no longer synchronized. Therefore, you will not get the count value to be 0 always.

Using Semaphores as locks to prevent race conditions:

The semaphore helps us in locking access to a resource. Any string that wants to utilize the resource should call the obtain() method first. This should take place before it gets to the asset to gain the bolt. Once the string finishes with the asset, it must call the discharge() method to discharge the bolt.

Sample program to implement using semaphores as lock to prevent race condition:

class FirstCode{
static int count = 0;
}
class FirstCode2 extends Thread{
Semaphore s;
String threadName;
public FirstCode2(Semaphore s, String threadName){
super(threadName);
this.s = s;
this.threadName = threadName;
}
@Override
public void run(){
if(this.getName().equal(“A”)){
System.out.println(“Starting ”+threadName);
try{
System.out.println(threadName + “is waiting for a permit.”);
s.acquire();
System.out.println(“threaName ”+ “gets a permit.”);
for(int i=0; i<5; i++){
Shared.count++;
Sstem.out.println(threadName + “:” + Shared.count);
Thread.sleep(10);
}
}catch(UnterruptedException exc){
System.out.println(exc);
}
System.out.println(threadName + “releases the permit.”);
s.release();
}
else{
System.out.println(“Starting” + threadName);
try{
System.out.println(threadName + “is waiting for a permit.”);
s.acquire();
System.out.println(threadName+ “gets a permit.”);
for(int i=0; i<5;i++){
Shared.count--;
System.out.println(threadName+ “:” +Shared.count);
Thread.sleep(10);
}
}catch(InterruptedException exc){
System.out.println(exc);
}
System.out.println(threadName+ “releases the permit.”);
s.release();
}
}
}
public class SemaphoreExample{
public static void main(String args[]) throws InterruptedException{
Semaphore s = newSemaphore(1);
FirstCode2 fc = new FirstCode2(s, “A”);
FirstCode2 fc2 = new FirstCode2(s, “B”); 
fc.start();
fc2.start();
fc.join();
fc2.join();
System.out.println(“count: ”+Shared.count);
}
}

Output:

Starting A
Starting B
B is waiting for a permit.
B gets a permit.
A is waiting for a permit.
B: -1
B: -2
B: -3
B: -4
B: -5
B releases the permit.
A gets a permit.
A: -4
A: -3
A: -2
A: -1
A: 0
A releases the permit.
count: 0

Summary

This is all about semaphore in java. Hope you enjoyed the article.

Leave a Reply

Your email address will not be published. Required fields are marked *