Java Semaphore
last modified February 15, 2025
In this article we show how to synchronize Java threads using Semaphore.
Semaphore is a synchronization tool that controls access to a
shared resource through a set of permits. It is useful for managing limited
resources, such as database connections or thread pools, where only a certain
number of threads can access the resource simultaneously.
A Semaphore is initialized with a number of permits. Threads can
acquire a permit using the acquire method and release it using the
release method. If no permits are available, the thread will block
until a permit is released by another thread.
Semaphore Example
The following example demonstrates how to use Semaphore to control
access to a shared resource.
import java.util.concurrent.Semaphore;
class SharedResource {
private final Semaphore semaphore;
public SharedResource(int permits) {
this.semaphore = new Semaphore(permits);
}
public void useResource() {
try {
semaphore.acquire(); // Acquire a permit
System.out.println(Thread.currentThread().getName() + " is using the resource");
Thread.sleep(2000); // Simulate resource usage
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
System.out.println(Thread.currentThread().getName() + " is releasing the resource");
semaphore.release(); // Release the permit
}
}
}
class Worker implements Runnable {
private final SharedResource sharedResource;
public Worker(SharedResource sharedResource) {
this.sharedResource = sharedResource;
}
@Override
public void run() {
sharedResource.useResource();
}
}
void main() throws InterruptedException {
SharedResource sharedResource = new SharedResource(2); // Allow 2 permits
Thread t1 = new Thread(new Worker(sharedResource), "Thread-1");
Thread t2 = new Thread(new Worker(sharedResource), "Thread-2");
Thread t3 = new Thread(new Worker(sharedResource), "Thread-3");
Thread t4 = new Thread(new Worker(sharedResource), "Thread-4");
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
}
In this program, a Semaphore is used to limit access to a shared
resource. Only two threads can access the resource at a time, as the semaphore
is initialized with two permits.
private final Semaphore semaphore;
public SharedResource(int permits) {
this.semaphore = new Semaphore(permits);
}
The SharedResource class is initialized with a
Semaphore that has a specified number of permits.
semaphore.acquire(); // Acquire a permit
A thread acquires a permit before accessing the resource. If no permits are available, the thread will block.
semaphore.release(); // Release the permit
After using the resource, the thread releases the permit, allowing other threads to acquire it.
SharedResource sharedResource = new SharedResource(2); // Allow 2 permits
The SharedResource is initialized with two permits, meaning only
two threads can access the resource simultaneously.
$ java Main.java Thread-1 is using the resource Thread-2 is using the resource Thread-1 is releasing the resource Thread-2 is releasing the resource Thread-3 is using the resource Thread-4 is using the resource Thread-3 is releasing the resource Thread-4 is releasing the resource
Semaphore with Fairness
The following example demonstrates how to use a Semaphore with fairness. When fairness is enabled, threads acquire permits in the order they requested them.
import java.util.concurrent.Semaphore;
class SharedResource {
private final Semaphore semaphore;
public SharedResource(int permits, boolean fair) {
this.semaphore = new Semaphore(permits, fair);
}
public void useResource() {
try {
semaphore.acquire(); // Acquire a permit
System.out.println(Thread.currentThread().getName() + " is using the resource");
Thread.sleep(2000); // Simulate resource usage
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
System.out.println(Thread.currentThread().getName() + " is releasing the resource");
semaphore.release(); // Release the permit
}
}
}
class Worker implements Runnable {
private final SharedResource sharedResource;
public Worker(SharedResource sharedResource) {
this.sharedResource = sharedResource;
}
@Override
public void run() {
sharedResource.useResource();
}
}
void main() throws InterruptedException {
SharedResource sharedResource = new SharedResource(2, true); // Allow 2 permits with fairness
Thread t1 = new Thread(new Worker(sharedResource), "Thread-1");
Thread t2 = new Thread(new Worker(sharedResource), "Thread-2");
Thread t3 = new Thread(new Worker(sharedResource), "Thread-3");
Thread t4 = new Thread(new Worker(sharedResource), "Thread-4");
t1.start();
t2.start();
t3.start();
t4.start();
t1.join();
t2.join();
t3.join();
t4.join();
}
In this program, the Semaphore is initialized with fairness
enabled. This ensures that threads acquire permits in the order they requested
them.
public SharedResource(int permits, boolean fair) {
this.semaphore = new Semaphore(permits, fair);
}
The Semaphore is initialized with fairness enabled, ensuring that
threads acquire permits in the order they requested them.
$ java Main.java Thread-1 is using the resource Thread-2 is using the resource Thread-1 is releasing the resource Thread-2 is releasing the resource Thread-3 is using the resource Thread-4 is using the resource Thread-3 is releasing the resource Thread-4 is releasing the resource
Thread Pool with Semaphore Example
The following example demonstrates how to use a Semaphore to
implement a thread pool. The thread pool limits the number of concurrent tasks
being executed to a fixed size.
import java.util.concurrent.Semaphore;
class Task implements Runnable {
private final int taskId;
private final Semaphore semaphore;
public Task(int taskId, Semaphore semaphore) {
this.taskId = taskId;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire(); // Acquire a permit
System.out.println("Task " + taskId + " is running on " + Thread.currentThread().getName());
Thread.sleep(2000); // Simulate task execution
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
System.out.println("Task " + taskId + " is completed");
semaphore.release(); // Release the permit
}
}
}
void main() throws InterruptedException {
int poolSize = 3; // Maximum number of concurrent tasks
Semaphore semaphore = new Semaphore(poolSize);
// Create and start 10 tasks
for (int i = 1; i <= 10; i++) {
Thread thread = new Thread(new Task(i, semaphore));
thread.start();
}
// Wait for all tasks to complete
Thread.sleep(10000); // Adjust sleep time as needed
System.out.println("All tasks completed");
}
In this program, a Semaphore is used to limit the number of
concurrent tasks being executed to a fixed size (3 in this case). Each task
acquires a permit before execution and releases it after completion.
semaphore.acquire(); // Acquire a permit
A task acquires a permit before starting execution. If no permits are available, the task will block until a permit is released.
semaphore.release(); // Release the permit
After completing execution, the task releases the permit, allowing another task to acquire it.
int poolSize = 3; // Maximum number of concurrent tasks Semaphore semaphore = new Semaphore(poolSize);
The Semaphore is initialized with a pool size of 3, meaning only 3
tasks can run concurrently.
$ java Main.java Task 1 is running on Thread-0 Task 2 is running on Thread-1 Task 3 is running on Thread-2 Task 1 is completed Task 4 is running on Thread-3 Task 2 is completed Task 5 is running on Thread-4 Task 3 is completed Task 6 is running on Thread-5 Task 4 is completed Task 7 is running on Thread-6 Task 5 is completed Task 8 is running on Thread-7 Task 6 is completed Task 9 is running on Thread-8 Task 7 is completed Task 10 is running on Thread-9 Task 8 is completed Task 9 is completed Task 10 is completed All tasks completed
Source
In this article we have shown how to synchronize Java threads using Semaphore for resource management.
Author
List all Java tutorials.