Java Concurrent API Packages | Semaphore

Semaphore is the most common type of Synchronization Object which many of us familiar with. Semaphore use the counter mechanism to control access to the shared resources.

Image Source : https://javadeveloperzone.com/spring-boot/spring-boot-tutorial/

If the value of counter is greater than 0 ( > 0), then access to the resource is allowed.
Access is denied if the value is equal to zero ( = 0).

The count only counts when the access is permitted to the shared resource.

Thus, in-order for a thread to get access to the shared resource then the semaphore must grant the permission. So first, the thread tried to access the permission from the Semaphore.

How it works?

If suppose the value of semaphore is greater than zero, then the thread acquires the permit and so the value of semaphore gets decremented preventing another thread to get access. Else, the thread waits till the count of semaphore is incremented from zero to get the access.

Once the thread completes its process with the shared resource, it releases the permit and so the semaphore count will get incremented.

Generally, Semaphore has the following two Constructors

Semaphore(int num);
Semaphore(int num, boolean how);

Here the parameter num specifies the initial number of permits the Semaphore can provide. The number of threads that can access the shared resource is based on this num value.

By default, the order of permission granted for the waiting thread is not in sequence. But when the Boolean value how is set to “True” then the order is maintained and access is given on that order.

To get access permit to the shared resources, the Thread need call the acquire() method. There are two ways to implement the acquire() method.

void acquire(int num) throws InterruptedException
void acquire() throws InterruptedException

Here the parameter num again specifies that any number of permits can be acquired. The Second form, only gives one permit. In most of the cases we use the Second form of acquire method.

Once the thread completes its task, it then releases it’s permit by calling the release() method. It comes in two varieties.

void release()
void release(int num)

Here, the first form releases only one permit, but the second form releases the number of permits which is passed as parameter.

In simple, if a thread wants to access any shared resource using semaphore then the thread must first try to acquire the permit to the resource using the acquire() method and once completed it task, it need to release the permit using the release() method.

Implementation Example

Here we are creating the Semaphore class with one as the permit counter value and a Shared Resource class with a Static variable Counter to simply check for the access permit acquiring and release.

public class semaphoreDemp {

public static void main(String[] args) {
Semaphore sem = new Semaphore(1);
new IncrementCounter(sem,"First Thread");
new DecrementCounter(sem, "Second Thread");
}
}


class Shared{
static int counter = 0;
}

We are then creating two separate thread classes that implements Runnable and try to acquire lock to the Shared class. One class is trying to increment the value of the Counter while other is to decrement the counter value.

Here, only one thread can acquire the permit as we have set the permit value for the semaphore as 1 so only one thread can access the Shared class at a time and the other Thread need to wait till the lock is released.

Let’s look into the implementation of IncrementCounter Class.

import java.util.concurrent.Semaphore;

public class IncrementCounter implements Runnable{
String name;
Semaphore sema;

IncrementCounter(Semaphore semaphore, String threadname){
sema = semaphore;
name = threadname;
new Thread(this).start();
}

/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/

@Override
public void run() {
System.out.println("Starting the "+ name);
try {
System.out.println("Waiting to acquire the permit "+ name);
sema.acquire();
System.out.println("Thread acquires the lock and got access "+ name);
for(int i =0;i< 5;i++){
Shared.counter++;
System.out.println("Value after increamented "+ name +" "+ Shared.counter);
Thread.sleep(100);
}
} catch (Exception e) {
System.out.println(e);
}
System.out.println("About to release teh permit "+ name);
sema.release();
}

}

Here we have implemented the Runnable class and overrided the Run() method. Inside the run Method, our thread is trying to call the acquire method to get the access permit to the Shared Class.

Once the permit is granted, then the value of Counter is increated for 5 times in a loop. Although the thread is sleeping for 100ms control will not be transferred to another waiting thread untill release() method is called.

Implementation of DecrementCounter class is also similar to that of Increment class, except inside the for loop for difference.

import java.util.concurrent.Semaphore;

public class DecrementCounter implements Runnable {
String name;
Semaphore sema;

DecrementCounter(Semaphore semaphore, String threadname){
sema = semaphore;
name = threadname;
new Thread(this).start();
}

/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/

@Override
public void run() {
System.out.println("Starting the "+ name);
try {
System.out.println("Waiting to acquire the permit "+ name);
sema.acquire();
System.out.println("Thread acquires the lock and got access "+ name);
for(int i =0;i< 5;i++){
Shared.counter--;
System.out.println("Value after increamented "+ name +" "+ Shared.counter);
Thread.sleep(100);
}
} catch (Exception e) {
System.out.println(e);
}
System.out.println("About to release teh permit "+ name);
sema.release();
}
}

When the semaphoreDemo class is Tested the following Output is received.

Starting the First Thread
Waiting to acquire the permit First Thread
Starting the Second Thread
Waiting to acquire the permit Second Thread
Thread acquires the lock and got access First Thread
Value after increamented First Thread 1
Value after increamented First Thread 2
Value after increamented First Thread 3
Value after increamented First Thread 4
Value after increamented First Thread 5
About to release teh permit First Thread
Thread acquires the lock and got access Second Thread
Value after increamented Second Thread 4
Value after increamented Second Thread 3
Value after increamented Second Thread 2
Value after increamented Second Thread 1
Value after increamented Second Thread 0
About to release teh permit Second Thread

This Output is not same at all the time because of the thread which is acquiring the lock may get differ at times.

Both the Threads are starting at the same time, but here First thread acquired the lock to the Shared Resource, so Second thread is waiting till first thread call release method and the Semaphore value is incremented to 1.

The Same program when ran for the second time, the output is different.

Starting the First Thread
Waiting to acquire the permit First Thread
Starting the Second Thread
Waiting to acquire the permit Second Thread
Thread acquires the lock and got access Second Thread
Value after increamented Second Thread -1
Value after increamented Second Thread -2
Value after increamented Second Thread -3
Value after increamented Second Thread -4
Value after increamented Second Thread -5
About to release teh permit Second Thread
Thread acquires the lock and got access First Thread
Value after increamented First Thread -4
Value after increamented First Thread -3
Value after increamented First Thread -2
Value after increamented First Thread -1
Value after increamented First Thread 0
About to release teh permit First Thread

Here Second Thread, acquired the permit first and locked the counter.So First thread waiting till the release and acquired the lock later.

Thus Semaphore helps us to set the number of Threads that can access the resource in the program itself.

Hope this helps you to get a better understanding about Semaphore and its use. Will discuss more about Synchronizers in the next blog. For the post order check this post.

Or

https://thefreshwrites.blogspot.com/2023/02/concurrent-utils-are-included-in.html

Thanks for reading.Happy learning ๐Ÿ˜„

Comments

Popular posts from this blog

Why Pointers are Eliminated in Java?

What is the advancement made in Hashmap in Java 8?

Integer.parseInt(“text”)