Semaphore
About
A Semaphore is a concurrency control mechanism used to restrict the number of threads that can access a shared resource simultaneously. It is part of the java.util.concurrent
package and is particularly useful when controlling access to a limited resource such as database connections, file systems, network sockets, or thread pools.
A Semaphore is a counter-based synchronization construct that allows a maximum number of threads to access a resource at the same time.
A semaphore maintains a set of permits.
A thread must acquire a permit before proceeding.
If no permits are available, the thread blocks until one is released.
A thread must release a permit after finishing its task
Why Use a Semaphore?
Control Concurrent Access – Limit the number of threads accessing a resource.
Prevent Overloading – Avoid exhausting system resources like database connections.
Fair Resource Allocation – Ensure fair access to shared resources.
Thread Pooling – Manage worker threads efficiently.
Avoiding Deadlocks & Starvation – Prevent resource starvation and improve concurrency control.
Types of Semaphores
1. Counting Semaphore
A Counting Semaphore is a semaphore that allows multiple permits, where each permit represents one unit of a shared resource. The number of available permits defines how many threads can access the resource at the same time.
How It Works?
When a thread wants to access the shared resource, it acquires a permit using
acquire()
.If all permits are taken, the thread blocks until a permit is available.
Once the thread is done using the resource, it releases the permit using
release()
, making it available for another thread.
Use Cases
Resource Pooling: Database connection pools, file system access, network socket management.
Rate Limiting: Controlling API requests or access to limited resources.
Thread Synchronization: Ensuring that only a specific number of threads execute a particular section of code.
Example Implementation
Only 3 threads can acquire a permit and proceed simultaneously.
The 4th thread must wait until one of the previous threads releases a permit.
This is useful for controlling concurrent access to limited resources.
2. Binary Semaphore (Mutex)
A Binary Semaphore, also known as a Mutex (Mutual Exclusion), is a semaphore with only one permit (0
or 1
). This ensures that only one thread can access the critical section at a time.
How It Works?
When a thread acquires the permit, the semaphore count goes to 0, preventing other threads from acquiring it.
Once the thread releases the permit, the count becomes 1, allowing another thread to acquire it.
Binary semaphores do not track ownership, meaning any thread can release a permit, which may lead to incorrect behavior.
Use Cases
Mutual Exclusion: Ensuring that only one thread enters a critical section at a time.
Alternative to Locks: Provides similar functionality to
synchronized
orReentrantLock
.Thread Signaling: Can be used for thread coordination, where one thread signals another to proceed.
Example Implementation
Only one thread at a time can enter the critical section.
The second thread must wait until the first thread releases the permit.
Works similarly to
synchronized
andReentrantLock
but does not provide reentrancy.
Fair vs. Unfair Semaphore
Fair Semaphore (FIFO Order)
By default, Semaphore
is unfair, meaning threads may not acquire permits in order.
If fairness is required, pass true
in the constructor to enforce FIFO order.
Fair semaphore prevents starvation but may reduce performance.
Unfair Semaphore (Default)
Threads may acquire permits out of order.
Higher performance but possible starvation.
Releasing More Permits Than Acquired (Over-Release Issue)
Unlike ReentrantLock
or synchronized
, a semaphore does not track which thread acquired a permit, meaning a thread can mistakenly release more permits than it acquired.
A thread can release permits it never acquired, causing logical inconsistencies.
This makes semaphores less strict than locks.
Semaphore vs. ReentrantLock vs. Synchronized
Control
Limits threads accessing a resource
Full thread control
Automatic
Fairness
FIFO (if enabled)
FIFO (if enabled)
Always unfair
Interruptibility
No
Yes
No
Timeout Support
tryAcquire()
tryLock()
No timeout
Reentrancy
No
Yes
Yes
Condition Variables
No
Condition
wait()/notify()
Best Practices
Use a semaphore for resource pooling (e.g., database connections).
Avoid over-releasing permits, as it leads to incorrect behavior.
Prefer
tryAcquire()
for non-blocking attempts.Use fair semaphores when starvation must be avoided.
Use binary semaphores for simple mutual exclusion instead of locks.
Last updated
Was this helpful?