Concurrency in Java: What is "Semaphore" and its use?
- 4.6/5
- 8115
- Jul 20, 2024
A Semaphore is a thread synchronization construct that can be used to limit the number of concurrent threads accessing a specific resource.
In general, a semaphore maintains a set of permits, each acquire() will take a permit from semaphore, each release() will return back the permit to the semaphore.
If permits are not available, acquire() will block until one is available.
Java 5 comes with a built-in Semaphore implementations in the java.util.concurrent package, so you don't have to implement your own.
There are two constructors in the java.util.concurrent.Semaphore class.
Semaphore(int num)
The "num" parameter specifies the initial permit count or the number of threads that can access a shared resource at a time.
Semaphore(int num, boolean how)
By default, all blocked threads are granted a permit in an undefined order. By setting "how" to "true", you can ensure that waiting threads are granted a permit in the order in which they requested access.
Semaphore example
Using Semaphores as Locks
A semaphore initialized to one, and which is used such that it only has at most one permit available, can be used as a lock.
package concurrency.semaphore; | |
import java.util.concurrent.Semaphore; | |
public class SemaphoresAsLock { | |
public static void main(String[] args) throws InterruptedException { | |
Semaphore binarySemaphore = new Semaphore(1); | |
Counter counter = new Counter(binarySemaphore); | |
CounterIncrementer counterIncrementer = new CounterIncrementer(counter); | |
CounterDecrementer counterDecrementer = new CounterDecrementer(counter); | |
counterIncrementer.start(); | |
counterDecrementer.start(); | |
counterIncrementer.join(); | |
counterDecrementer.join(); | |
System.out.println("Count: " + counter.getCount()); | |
} | |
private static class CounterIncrementer extends Thread { | |
private Counter counter; | |
public CounterIncrementer(Counter counter) { | |
this.counter = counter; | |
} | |
public void run() { | |
for (int i = 0; i < 10000; i++) | |
counter.increment(); | |
} | |
} | |
private static class CounterDecrementer extends Thread { | |
private Counter counter; | |
public CounterDecrementer(Counter counter) { | |
this.counter = counter; | |
} | |
public void run() { | |
for (int i = 0; i < 10000; i++) | |
counter.decrement(); | |
} | |
} | |
private static class Counter { | |
private Semaphore semaphore; | |
private int count; | |
public Counter(Semaphore semaphore) { | |
this.semaphore = semaphore; | |
} | |
public int getCount() { | |
return count; | |
} | |
public void increment() { | |
try { | |
semaphore.acquire(); | |
count++; | |
} catch (InterruptedException e) { | |
throw new RuntimeException(e); | |
} finally { | |
semaphore.release(); | |
} | |
} | |
public synchronized void decrement() { | |
try { | |
semaphore.acquire(); | |
count--; | |
} catch (InterruptedException e) { | |
throw new RuntimeException(e); | |
} finally { | |
semaphore.release(); | |
} | |
} | |
} | |
} |
Count: 0
Using Semaphores for Signaling
Semaphore can also be used to send signals between threads - call acquire() in place of wait() and release() in place of notify().
If the call to release() happens before the call to acquire(), the thread calling acquire() will still get the available permit, released by the other thread calling release().