Java Semaphore

Java Semaphore tutorial shows how to synchronize Java threads using Semaphore for resource management.

Java Semaphore

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.

Main.java

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.

Main.java

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.

Main.java

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

Java Semaphore - reference

In this article we have shown how to synchronize Java threads using Semaphore for resource management.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Java tutorials.

ad ad