The java.util.concurrent.Semaphore
class is a counting semaphore. This means that it has two main methods:
acquire()
release()
A counting semaphore is initialized with a specified number of "permits". Each call to acquire()
takes away one permit from the calling thread. Each call to release()
returns one permit to the semaphore. Therefore, without any release()
calls, at most N threads can pass through the acquire()
method, where N is the specified number of permits when the semaphore was initialized. These permits are simply a simple counter. There's nothing special here.
Usage of Semaphore#
Semaphores have two main uses:
- Protecting a critical (code) section from being entered by more than N threads at a time.
- Signaling between two threads.
Protecting a critical section#
If you use a semaphore to protect a critical section, the code that tries to enter this section will usually first try to acquire a permit before entering the critical section (code block), and then release the permit after it has finished executing. For example:
Semaphore semaphore = new Semaphore(1);
//critical section
semaphore.acquire();
...
semaphore.release();
Signaling between threads#
If you use a semaphore to signal between two threads, you should typically have one thread call the acquire()
method, while the other thread calls the release()
method.
If there are no permits available, the acquire()
call will block until another thread releases a permit. Similarly, a release()
call will block if there are no more permits to be released.
This can be used to coordinate multiple threads. For example, if thread 1 inserts an object into a shared list and then calls acquire()
, while thread 2 calls release()
before retrieving an object from the list, you have effectively created a blocking queue. The number of available permits in the semaphore is equivalent to the number of elements the blocking queue can hold.
Fairness#
There is no guarantee that threads will be able to acquire permits from the semaphore in a fair manner. In other words, there is no guarantee that the first thread to call acquire()
will be the first thread to acquire a permit. If the first thread is blocked while waiting for a permit, and another thread comes along and releases a permit, it may acquire the permit before the first thread.
If you want to enforce fairness, the Semaphore class has a constructor with a boolean parameter that can be used to indicate whether fairness should be enforced. Enforcing fairness can impact concurrency performance, so only enable it if you really need it.
Here is an example of creating a Semaphore in fair mode:
Semaphore semaphore = new Semaphore(1, true);
More methods#
The java.util.concurrent.Semaphore
class has many other methods, such as:
availablePermits()
acquireUninterruptibly()
drainPermits()
hasQueuedThreads()
getQueuedThreads()
tryAcquire()
and so on.