Semaphore is a synchronization aid that can be used for inter-communication among threads or to restrict access to resource.
An implementation of Semaphore in Java is available for out of box use and it is part of java.util.concurrent
package.
Counting semaphore
The implementation of semaphore readily available in the Java concurrency package is a counting semaphore.
Conceptually, a semaphore maintains a set of permits. When a semaphore is created it is created with a given number of permits. The working of semaphore in Java can be explained using the following steps-
- A thread that wants to access a shared resource tries to acquire a permit using the
acquire()
method. - If permit is available or in other words if semaphore count is greater than zero then the thread acquires a permit otherwise thread is blocked.
- With every successful acquisition of the permit the count is also decremented. If count becomes zero then no permit can be given.
- When the thread is done with the shared resource it can release the acquired permit using the
release()
method. This increments the semaphore's count. - Any blocking thread waiting to acquire a permit can get a permit once count is more than zero.
Java semaphore constructors
- Semaphore(int permits)- Creates a Semaphore with the given number of permits and nonfair fairness setting.
- Semaphore(int permits, boolean fair)- Creates a Semaphore with the given number of permits and the given fairness setting.
Semaphore example in Java
Let’s say there is a method that is computation heavy and you want to restrict access to this method to 2 threads at any given time. In this scenario you can use Semaphore created with 2 permits.
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class SemaphoreDemo { public static void main(String[] args) { // Semaphore with 2 permits Semaphore s = new Semaphore(2); ExecutorService ex = Executors.newFixedThreadPool(4); // Executing 6 times with a pool of 4 threads for(int i = 0; i < 6; i++) { ex.execute(new HeavyDuty(s)); } ex.shutdown(); } } class HeavyDuty implements Runnable{ private Semaphore s; HeavyDuty(Semaphore s){ this.s = s; } @Override public void run() { try { s.acquire(); System.out.println("Permit ACQUIRED by " + Thread.currentThread().getName()); doProcessing(); System.out.println("Permit released by " + Thread.currentThread().getName()); s.release(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private void doProcessing() throws InterruptedException{ System.out.println("doing heavy computation processing "); Thread.sleep(5000); } }Output
Permit ACQUIRED by pool-1-thread-1 doing heavy computation processing Permit ACQUIRED by pool-1-thread-2 doing heavy computation processing Permit released by pool-1-thread-1 Permit ACQUIRED by pool-1-thread-4 doing heavy computation processing Permit released by pool-1-thread-2 Permit ACQUIRED by pool-1-thread-3 doing heavy computation processing Permit released by pool-1-thread-4 Permit ACQUIRED by pool-1-thread-1 doing heavy computation processing Permit released by pool-1-thread-3 Permit ACQUIRED by pool-1-thread-2 doing heavy computation processing Permit released by pool-1-thread-1 Permit released by pool-1-thread-2
As you can see at any given time permits are acquired by 2 threads.
Binary Semaphore
A semaphore in Java created with only one permit 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.
Binary Semaphore example in Java
Here is a simple binary semaphore example where a shared counter is used among multiple threads. Binary semaphore let only one thread access the shared resource at any given time.
public class SemaphoreDemo { public static void main(String[] args) { // Semaphore with 1 permit Semaphore s = new Semaphore(1); SharedCounter counter = new SharedCounter(s); for(int i = 0; i < 6; i++) { new Thread(counter).start(); } } } class SharedCounter implements Runnable{ private int c = 0; private Semaphore s; SharedCounter(Semaphore s){ this.s = s; } @Override public void run() { try { s.acquire(); incrCounter(); s.release(); }catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // incrementing the value public void incrCounter() throws InterruptedException{ Thread.sleep(10); System.out.println("Value for Thread After increment - " + Thread.currentThread().getName() + " " + ++c); } }Output
Value for Thread After increment - Thread-0 1 Value for Thread After increment - Thread-1 2 Value for Thread After increment - Thread-2 3 Value for Thread After increment - Thread-3 4 Value for Thread After increment - Thread-4 5 Value for Thread After increment - Thread-5 6
To see how threads can interfere you can comment the acquire and release methods with in the run() method.
public void run() { try { //s.acquire(); incrCounter(); //s.release(); }catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
Running after commenting gives the following output for one run where same count is displayed for 2 threads.
Value for Thread After increment - Thread-4 1 Value for Thread After increment - Thread-2 2 Value for Thread After increment - Thread-0 3 Value for Thread After increment - Thread-5 4 Value for Thread After increment - Thread-3 1 Value for Thread After increment - Thread-1 2
Methods in Java Semaphore class
Some of the important methods in Semaphore class in Java are as follows-
- acquire()- Acquires a permit from this semaphore, blocking until one is available, or the thread is interrupted.
- acquire(int permits)- Acquires the given number of permits from this semaphore, blocking until all are available, or the thread is interrupted.
- availablePermits()- Returns the current number of permits available in this semaphore.
- drainPermits()- Acquires and returns all permits that are immediately available, or if negative permits are available, releases them.
- getQueuedThreads()- Returns a collection containing threads that may be waiting to acquire.
- isFair()- Returns true if this semaphore has fairness set true.
- release()- Releases a permit, returning it to the semaphore.
- tryAcquire()- Acquires a permit from this semaphore, only if one is available at the time of invocation.
- tryAcquire(int permits)- Acquires the given number of permits from this semaphore, only if all are available at the time of invocation.
That's all for the topic Semaphore in Java With Examples. If something is missing or you have something to share about the topic please write a comment.
You may also like
- CountDownLatch in Java With Examples
- PriorityBlockingQueue in Java With Examples
- Variable Scope in Java Lambda Expression
- Java Stream skip() Method With Examples
- Java TemporalAdjusters Class With Examples
- Java Multi-Catch Exception With Examples
- Injecting List, Set or Map in Spring
- Spring Boot Microservices + Hystrix Circuit Breaker
- HTTP GET Method in React - fetch, Axios
- Dynamic Route in React
No comments:
Post a Comment