Semaphore restricts the number of threads that can concurrently access a shared resource. It allocates permits for allowing access to the shared resource.
When we create an instance of a Semaphore, we specify the total number of permits.
In the below, we have created a Semaphore with 5 permits which means only 5 threads can concurrently access the resource.
Semaphore semaphore = new Semaphore(5);
A thread need to acquire a permit to access the resource.
semaphore.acquire();
The number of permits reduces by 1, when a thread acquires a permit.
The number of permits increases by 1, when a thread releases the permit.
semaphore.release();
When the number of permits becomes zero, the thread trying to acquire a permit remains in blocked state until some other thread releases it’s permit.
Realtime example
Let’s take a realtime example in today’s world.
Due to COVID 19, different restrictions have been put around the World. For our use case, we will take an example of a Grocery Store.
So, this Grocery store allows maximum of 5 customers at any point of time inside it. Now, let’s say, we have 10 customers outside the store who want to barge into the store.
The store will initially allow 5 customers out of 10 to get into it, then when one customer comes out , it allows one of the waiting customers to get in.
Here , in this case, the Grocery Store can be considered as the shared resource, customer as a Runnable(which is executed by a thread) and number of permits as 5.
Let’s implement this in Java 🙂
We will use ExecutorService to create a Fixed Thread Pool for our use case.
package com.java.concurrent;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class SemaphoreTest {
public static void main(String[] args) throws InterruptedException {
Semaphore semaphore = new Semaphore(5);
ExecutorService executorService = Executors.newFixedThreadPool(50);
IntStream.range(1,10).forEach(i->executorService.execute(new Customer(semaphore,i)));
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MILLISECONDS);
}
}
class Customer implements Runnable{
private Semaphore semaphore;
private int customerNumber;
public Customer(Semaphore semaphore, int customerNumber) {
this.semaphore= semaphore;
this.customerNumber= customerNumber;
}
@Override
public void run() {
try {
// Trying to acquire a permit. If permit is not available , the thread will be blocked here
semaphore.acquire();
System.out.println("Customer:"+customerNumber +" is allowed inside the Grocery Store");
// Release the permit.
semaphore.release();
System.out.println("Customer:"+customerNumber +" goes outside the Grocery Store");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Customer:1 is allowed inside the Grocery Store
Customer:4 is allowed inside the Grocery Store
Customer:5 is allowed inside the Grocery Store
Customer:4 goes outside the Grocery Store
Customer:5 goes outside the Grocery Store
Customer:2 is allowed inside the Grocery Store
Customer:1 goes outside the Grocery Store
Customer:3 is allowed inside the Grocery Store
Customer:7 is allowed inside the Grocery Store
Customer:7 goes outside the Grocery Store
Customer:8 is allowed inside the Grocery Store
Customer:2 goes outside the Grocery Store
Customer:6 is allowed inside the Grocery Store
Customer:9 is allowed inside the Grocery Store
Customer:8 goes outside the Grocery Store
Customer:3 goes outside the Grocery Store
Customer:9 goes outside the Grocery Store
Customer:6 goes outside the Grocery Store
Constructors
Methods
The methods marked in red are blocking in nature in the sense that the thread is blocked until a permit is acquired whereas the methods marked in green are non blocking, here the thread is not blocked.