Appearance
Semaphore
In the previous discussions, we covered various lock implementations. The primary purpose of a lock is to protect a limited resource, ensuring that at any given time only one thread can access it (ReentrantLock), or only one thread can write to it (ReadWriteLock).
There is another scenario where a limited resource must ensure that no more than N threads can access it simultaneously. For example, limiting the number of database connections to 100 at any given time or allowing a maximum of 10 users to download simultaneously.
Implementing such a quantity restriction using an array of locks would be cumbersome. Instead, Semaphore is ideal for handling this situation. It allows setting a limit on the number of concurrent accesses. For instance, to permit a maximum of 3 threads to access simultaneously:
java
public class AccessLimitControl {
// Allow up to 3 threads to acquire a permit at any given time:
final Semaphore semaphore = new Semaphore(3);
public String access() throws Exception {
// If the number of permits is exceeded, other threads will wait here:
semaphore.acquire();
try {
// Perform some task:
return UUID.randomUUID().toString();
} finally {
semaphore.release();
}
}
}With Semaphore, we first call acquire() to obtain a permit and then use a try ... finally block to ensure that the permit is released in the finally section.
Calling acquire() might cause the thread to wait until a permit becomes available. The tryAcquire() method allows specifying a maximum wait time:
java
if (semaphore.tryAcquire(3, TimeUnit.SECONDS)) {
// Acquired a permit within 3 seconds:
try {
// Perform some task:
} finally {
semaphore.release();
}
}A Semaphore essentially acts as a counter that tracks the number of available permits, limiting the number of simultaneous accesses.
Summary
To limit access to a constrained resource, use a Semaphore to ensure that no more than N threads can access the resource at the same time.