1. Introduction
The concept of a semaphore was introduced by Edsger Dijkstra in 1965 and was used for the first time in the THEOS operating system. The semaphore mechanism is used to control the access to one or more shared resources. The java.util.Semaphore
class represents a counting semaphore which is initialized with a number of permits. Semaphore provides both blocking methods as well as unblocking method to acquire permits.
Semaphores are often used to restrict the number of threads that can access some resource. Semaphore maintains a set of permits. In order to access a shared resource, current thread must acquire a permit. If a permit is already exhausted by other threads then it has to wait until a permit is available.
When a thread wants to access one of these shared resources, first, it must acquire a permit from semaphore. Permit here doesn’t mean some object but means a counter maintained by semaphore. When a thread has finished, item goes back to the pool and the permit is returned to the semaphore. Once permit is available, other threads can try to gain the permit and access resource. Two methods used for this purpose are acquire()
and release()
. Method acquire()
is used to acquire a permit from semaphore and release()
is used to release permit.
Semaphore ensure happens-before mechanism. Actions performed by a thread before calling
release()
happens-before actions performed by another thread after callingacquire()
.
If the internal counter of the semaphore is greater than 0, the semaphore decrements the counter and allows access to the shared resource. A counter bigger than 0 means there are free resources that can be used, so the thread can access and use one
of them.
2. Binary semaphore
Binary semaphore is initialized to 1 and has at most one permit. It is called binary semaphore because only two possible states are possible, permit is available or not. One means permit is available, zero means it is not available.
Binary semaphore can act as a mutual exclusion lock.
3. Fairness in semaphore
The concept of fairness is used in Java to allow thread waiting for a longer period of time to gain access to the resource. In non-fair mode, when a synchronized resource is released, any waiting thread can get a chance to get the resource.
Semaphore class’ constructor also has an optional parameter for fairness. Setting the value as false means non-fair mode. In non-fair mode semaphore does not guarantee in which order thread will acquire permits. When fairness is set to true, semaphore guarantees that threads are selected to obtain permits in the order of first-in-first-out (FIFO). That is, the thread which invoked any of the acquire method earlier will get a chance earlier than the other threads to obtain permit.
The untimed
tryAcquire
methods do not honor the fairness setting. It is recommended to use semaphore with fairness as true else there may be a situation when the some threads have to wait for longer duration.Semaphore class allows to acquire and release multiple permits.
4. Semaphore example
In this simple example, we’ll create a binary semaphore which can act as a mutual exclusion lock. As already discussed, in binary semaphore the maximum permit count is set to 1.
If you observe the output, the thread which got the permit prints the values. Here, loop represents the action performed by the thread after getting permit.
package com.learnitweb; import java.util.concurrent.Semaphore; public class SemaphoreTest { // maximum 1 permit static Semaphore semaphore = new Semaphore(1); static class MyThread extends Thread { String name = ""; MyThread(String name) { this.name = name; } public void run() { try { System.out.println(name + ": acquiring lock..."); System.out.println(name + ": available Semaphore permits: " + semaphore.availablePermits()); semaphore.acquire(); System.out.println(name + ": got the permit."); try { for (int i = 1; i <= 5; i++) { System.out.println(name + " : is executing. Value of i: " + i + ", Available permits: " + semaphore.availablePermits()); // sleep for 2 second Thread.sleep(2000); } } finally { // release permit System.out.println(name + ": releasing lock."); semaphore.release(); System.out.println(name + ": available permits: " + semaphore.availablePermits()); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { System.out.println("Total available permits: " + semaphore.availablePermits()); MyThread thread1 = new MyThread("thread 1"); thread1.start(); MyThread thread2 = new MyThread("thread 2"); thread2.start(); MyThread thread3 = new MyThread("thread 3"); thread3.start(); } }
Output
Total available permits: 1
thread 2: acquiring lock...
thread 2: available Semaphore permits: 1
thread 2: got the permit.
thread 2 : is executing. Value of i: 1, Available permits: 0
thread 3: acquiring lock...
thread 3: available Semaphore permits: 0
thread 1: acquiring lock...
thread 1: available Semaphore permits: 0
thread 2 : is executing. Value of i: 2, Available permits: 0
thread 2 : is executing. Value of i: 3, Available permits: 0
thread 2 : is executing. Value of i: 4, Available permits: 0
thread 2 : is executing. Value of i: 5, Available permits: 0
thread 2: releasing lock.
thread 2: available permits: 1
thread 3: got the permit.
thread 3 : is executing. Value of i: 1, Available permits: 0
thread 3 : is executing. Value of i: 2, Available permits: 0
thread 3 : is executing. Value of i: 3, Available permits: 0
thread 3 : is executing. Value of i: 4, Available permits: 0
thread 3 : is executing. Value of i: 5, Available permits: 0
thread 3: releasing lock.
thread 3: available permits: 1
thread 1: got the permit.
thread 1 : is executing. Value of i: 1, Available permits: 0
thread 1 : is executing. Value of i: 2, Available permits: 0
thread 1 : is executing. Value of i: 3, Available permits: 0
5. Conclusion
In this tutorial, we discussed Semaphore
class provided in java.util.concurrent
package. We discussed how to use a Semaphore with example. This class is very helpful in scenarios where you want to keep a limited number of threads to be working on a resource. You can also use this class as a mutual exclusion lock.