Week 145 — What is the Semaphore class and what is it used for?

Question of the Week #145
What is the Semaphore class and what is it used for?
5 Replies
Eric McIntyre
Eric McIntyre2w ago
A semaphore is a concurrency utility that can be configured with a given number of initially available permits. The acquire() method can be used to wait for one permit to be available and reduce the number of permits by 1 once a permit is available. On the other hand, the release() method can be used to mark one permit as available. It is also possible to use the acquire and release methods in a way that acquires/releases multiple permits at once. Semaphores can be used in scenarios where only a limited amount of threads should access a resource concurrently. They are typically used in try-finally blocks.
Semaphore semaphore = new Semaphore(3);
Semaphore semaphore = new Semaphore(3);
semaphore.acquire();
try{
// code here
// this code can run up to 3 times concurrently because a permit is used while this code here is running and there are only 3 permits available in this semaphore

}finally {
semaphore.release();
}
semaphore.acquire();
try{
// code here
// this code can run up to 3 times concurrently because a permit is used while this code here is running and there are only 3 permits available in this semaphore

}finally {
semaphore.release();
}
For example, the following code will create 10 threads trying to do some work using a semaphore that only allows 3 permits. This effectively limits it to 3 threads doing the work at the same time and the other threads need to wait until permits are available again.
Semaphore semaphore = new Semaphore(3);
Runnable r = () -> {
System.out.println("[" + Thread.currentThread().getName() + "] trying to acquire semaphore");
try {
semaphore.acquire();
}catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
try {
// do some work
System.out.println("[" + Thread.currentThread().getName() + "] Semaphore acquired - doing 'work' for 1 second");
TimeUnit.SECONDS.sleep(1);
System.out.println("[" + Thread.currentThread().getName() + "] So much useful work has been done - the semaphore will now be released");
}catch(InterruptedException e){
Thread.currentThread().interrupt();
} finally {
semaphore.release();
System.out.println("[" + Thread.currentThread().getName() + "] Semaphore released");
}
};

for(int i=0;i<10;i++) {
new Thread(r).start();
}
Semaphore semaphore = new Semaphore(3);
Runnable r = () -> {
System.out.println("[" + Thread.currentThread().getName() + "] trying to acquire semaphore");
try {
semaphore.acquire();
}catch(InterruptedException e) {
Thread.currentThread().interrupt();
}
try {
// do some work
System.out.println("[" + Thread.currentThread().getName() + "] Semaphore acquired - doing 'work' for 1 second");
TimeUnit.SECONDS.sleep(1);
System.out.println("[" + Thread.currentThread().getName() + "] So much useful work has been done - the semaphore will now be released");
}catch(InterruptedException e){
Thread.currentThread().interrupt();
} finally {
semaphore.release();
System.out.println("[" + Thread.currentThread().getName() + "] Semaphore released");
}
};

for(int i=0;i<10;i++) {
new Thread(r).start();
}
When running the code above, one gets output similar to the following:
[Thread-1] trying to acquire semaphore
[Thread-7] trying to acquire semaphore
[Thread-1] Semaphore acquired - doing 'work' for 1 second
[Thread-7] Semaphore acquired - doing 'work' for 1 second
[Thread-6] trying to acquire semaphore
[Thread-6] Semaphore acquired - doing 'work' for 1 second
[Thread-5] trying to acquire semaphore
[Thread-4] trying to acquire semaphore
[Thread-3] trying to acquire semaphore
[Thread-0] trying to acquire semaphore
[Thread-2] trying to acquire semaphore
[Thread-9] trying to acquire semaphore
[Thread-8] trying to acquire semaphore
[Thread-7] So much useful work has been done - the semaphore will now be released
[Thread-1] So much useful work has been done - the semaphore will now be released
[Thread-1] Semaphore released
[Thread-6] So much useful work has been done - the semaphore will now be released
[Thread-7] Semaphore released
[Thread-3] Semaphore acquired - doing 'work' for 1 second
[Thread-5] Semaphore acquired - doing 'work' for 1 second
[Thread-4] Semaphore acquired - doing 'work' for 1 second
[Thread-6] Semaphore released
[Thread-3] So much useful work has been done - the semaphore will now be released
[Thread-5] So much useful work has been done - the semaphore will now be released
[Thread-4] So much useful work has been done - the semaphore will now be released
[Thread-1] trying to acquire semaphore
[Thread-7] trying to acquire semaphore
[Thread-1] Semaphore acquired - doing 'work' for 1 second
[Thread-7] Semaphore acquired - doing 'work' for 1 second
[Thread-6] trying to acquire semaphore
[Thread-6] Semaphore acquired - doing 'work' for 1 second
[Thread-5] trying to acquire semaphore
[Thread-4] trying to acquire semaphore
[Thread-3] trying to acquire semaphore
[Thread-0] trying to acquire semaphore
[Thread-2] trying to acquire semaphore
[Thread-9] trying to acquire semaphore
[Thread-8] trying to acquire semaphore
[Thread-7] So much useful work has been done - the semaphore will now be released
[Thread-1] So much useful work has been done - the semaphore will now be released
[Thread-1] Semaphore released
[Thread-6] So much useful work has been done - the semaphore will now be released
[Thread-7] Semaphore released
[Thread-3] Semaphore acquired - doing 'work' for 1 second
[Thread-5] Semaphore acquired - doing 'work' for 1 second
[Thread-4] Semaphore acquired - doing 'work' for 1 second
[Thread-6] Semaphore released
[Thread-3] So much useful work has been done - the semaphore will now be released
[Thread-5] So much useful work has been done - the semaphore will now be released
[Thread-4] So much useful work has been done - the semaphore will now be released
Eric McIntyre
Eric McIntyre2w ago
[Thread-4] Semaphore released
[Thread-0] Semaphore acquired - doing 'work' for 1 second
[Thread-3] Semaphore released
[Thread-9] Semaphore acquired - doing 'work' for 1 second
[Thread-2] Semaphore acquired - doing 'work' for 1 second
[Thread-5] Semaphore released
[Thread-0] So much useful work has been done - the semaphore will now be released
[Thread-9] So much useful work has been done - the semaphore will now be released
[Thread-8] Semaphore acquired - doing 'work' for 1 second
[Thread-2] So much useful work has been done - the semaphore will now be released
[Thread-0] Semaphore released
[Thread-2] Semaphore released
[Thread-9] Semaphore released
[Thread-8] So much useful work has been done - the semaphore will now be released
[Thread-8] Semaphore released
[Thread-4] Semaphore released
[Thread-0] Semaphore acquired - doing 'work' for 1 second
[Thread-3] Semaphore released
[Thread-9] Semaphore acquired - doing 'work' for 1 second
[Thread-2] Semaphore acquired - doing 'work' for 1 second
[Thread-5] Semaphore released
[Thread-0] So much useful work has been done - the semaphore will now be released
[Thread-9] So much useful work has been done - the semaphore will now be released
[Thread-8] Semaphore acquired - doing 'work' for 1 second
[Thread-2] So much useful work has been done - the semaphore will now be released
[Thread-0] Semaphore released
[Thread-2] Semaphore released
[Thread-9] Semaphore released
[Thread-8] So much useful work has been done - the semaphore will now be released
[Thread-8] Semaphore released
📖 Sample answer from dan1st
Eric McIntyre
Eric McIntyre2w ago
It's a package that manages access to a shared resource by controlling the number of threads that can access it concurrently. It implements the semaphore concept from computer science, which acts as a counter to regulate access to a resource pool
Submission from harshit_310
Eric McIntyre
Eric McIntyre2w ago
The java.util.concurrent.Semaphore class is a threading primitive that restricts access to a protected resource. It is similar to a synchronized method or block, but is much more flexible. In the case of synchronized, only a single thread can hold the monitor object's lock at any one time -- all other threads that might want to acquire the lock must wait until that first thread releases it. This can cause bottlenecks and deadlocks. The Semphore improves this situation by defining a structure that has a fixed number of available "permits". Resources that want to limit the number of active users of the resource -- such as a connection pool -- use a semaphore to ensure that users either receive an available resource or wait until one becomes available. It can also ensure that waiting users gain access to the resource in "request" order. The number of available permits is set at construction time:
private final Semaphore availableResources = new Semaphore(20);
private final Semaphore availableResources = new Semaphore(20);
The resource manager then uses the semaphore in methods that acquire or release a resource:
public Resource checkout() {
availableResources.acquire(); // pause the thread until one is available
return nextAvailable(); // the internal implementation of the resource manager
}

public void release(Resource resource) {
if (returnResource(resource) { // return the resource to the available pool
availableResources.release(); // mark the resource as returned, and release a permit to the next waiting thread
}
}
public Resource checkout() {
availableResources.acquire(); // pause the thread until one is available
return nextAvailable(); // the internal implementation of the resource manager
}

public void release(Resource resource) {
if (returnResource(resource) { // return the resource to the available pool
availableResources.release(); // mark the resource as returned, and release a permit to the next waiting thread
}
}
Eric McIntyre
Eric McIntyre2w ago
By default, a Semaphore is "unfair" -- it will not make any guarantees about which waiting thread receives the next available resource. This has lower overhead and higher throughput, but could result in resource starvation for some threads. Semaphores that are managing access to a limited resource like a connection pool should generally be "fair". This is accomplished by passing a boolean parameter to the constructor:
private final Semaphore availableResources = new Semaphore(20, true); // true for fair, false for unfair
private final Semaphore availableResources = new Semaphore(20, true); // true for fair, false for unfair
⭐ Submission from dangerously_casual

Did you find this page helpful?