#
Semaphores in Java
This tutorial explains the usage of Semaphores in Java.
Info
The Semaphores limits the number of threads that can access a resource.
For instance, we can think of a parking lot. This parking lot has a finite number of parking spaces. We can think of a rule to not allow in the parking lot a number of cars bigger than the number of parking spaces. This could be a use case for using semaphores.
In my example, I start from a simple Spring Boot application created using Spring initializr.
Let's take a look at the following example.
I will create 2 thread types:
package com.exampe.java;
import java.util.concurrent.Semaphore;
public class MyThreadAdd extends Thread {
Integer threadNumber;
Semaphore semaphoreWith4Permits;
public MyThreadAdd(Integer threadNumber, Semaphore semaphoreWith4Permits) {
this.threadNumber = threadNumber;
this.semaphoreWith4Permits = semaphoreWith4Permits;
}
public void run()
{
System.out.println("Thread " + this.threadNumber + " is running...");
for (int i = 1; i <= 5; i++) {
try {
semaphoreWith4Permits.acquire();
MyStaticCounter.add(1, this.threadNumber);
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
System.out.println("Thread " + this.threadNumber + " stopped.");
}
}
package com.exampe.java;
import java.util.concurrent.Semaphore;
public class MyThreadSubtract extends Thread {
Integer threadNumber;
Semaphore semaphoreWith4Permits;
public MyThreadSubtract(Integer threadNumber, Semaphore semaphoreWith4Permits) {
this.threadNumber = threadNumber;
this.semaphoreWith4Permits = semaphoreWith4Permits;
}
public void run()
{
System.out.println("Thread " + this.threadNumber + " is running...");
for (int i = 1; i <= 5; i++) {
try {
MyStaticCounter.subtract(1, this.threadNumber);
semaphoreWith4Permits.release();
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
};
System.out.println("Thread " + this.threadNumber + " stopped.");
}
}
I will create a class which keeps the counter and which must be accessed by ONLY a thread at a time.
package com.exampe.java;
import org.springframework.stereotype.Component;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class MyStaticCounter {
static int counter = 0;
// Will lock the unique MyStaticCounter instance
public static synchronized void add(Integer value, Integer threadNumber) throws InterruptedException {
counter = counter + value;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("mm.ss");
LocalTime today = LocalTime.now();
String timeString = today.format(formatter);
System.out.println("Time:"+timeString+"/ Thread #" + threadNumber + " /add/ Counter="+counter);
Thread.sleep(2000);
}
// Will lock the unique MyStaticCounter instance
public static synchronized void subtract(Integer value, Integer threadNumber)throws InterruptedException {
counter = counter - value;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("mm.ss");
LocalTime today = LocalTime.now();
String timeString = today.format(formatter);
System.out.println("Time:"+timeString+"/ Thread #" + threadNumber + " /subtract/ Counter="+counter);
Thread.sleep(2000);
}
}
Here is my main class :
package com.exampe.java;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import java.util.concurrent.Semaphore;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) throws InterruptedException {
SpringApplication.run(DemoApplication.class, args);
System.out.println("--------------------------------------------");
Semaphore semaphoreWith4Permits = new Semaphore(4);
MyThreadAdd t1 = new MyThreadAdd(1, semaphoreWith4Permits);
MyThreadSubtract t2 = new MyThreadSubtract(2, semaphoreWith4Permits);
MyThreadAdd t3 = new MyThreadAdd(3, semaphoreWith4Permits);
MyThreadAdd t4 = new MyThreadAdd(4, semaphoreWith4Permits);
t1.start();t2.start();t3.start();t4.start();
// Waiting for the threads to complete
t1.join(); t2.join(); t3.join();t4.join();
System.out.println("--------------------------------------------");
System.out.println("End of the execution");
}
}
When I run the code I get (is generally different at each run):
--------------------------------------------
Thread 4 is running...
Thread 2 is running...
Thread 3 is running...
Thread 1 is running...
Time:41.49/ Thread #4 /add/ Counter=1
Time:41.51/ Thread #2 /subtract/ Counter=0
Time:41.53/ Thread #1 /add/ Counter=1
Time:41.55/ Thread #3 /add/ Counter=2
Time:41.57/ Thread #1 /add/ Counter=3
Time:41.59/ Thread #2 /subtract/ Counter=2
Time:42.01/ Thread #4 /add/ Counter=3
Time:42.03/ Thread #2 /subtract/ Counter=2
Time:42.05/ Thread #3 /add/ Counter=3
Time:42.07/ Thread #2 /subtract/ Counter=2
Time:42.09/ Thread #1 /add/ Counter=3
Time:42.11/ Thread #2 /subtract/ Counter=2
Time:42.13/ Thread #4 /add/ Counter=3
Thread 2 stopped.
Time:42.15/ Thread #3 /add/ Counter=4
- At one point the threads are blocked because it will not be possible to acquire a permit from the semaphore.
- It is possible to acquire/release more than 1 permit.
If the number of permits of a semaphore is set to 1, we are talking about a Mutex (Mutual Exclusion). It works as a synchronized block.
We can use Semaphore(int num, boolean how)
. If the boolean value is set to true,
it permits waiting for the threads in the order they requested the access.