What is a semaphore
Semaphore is used to limit access to a resource to a fixed number of threads.
With semaphore you can ensure that a code is executed concurrently at most by the number of threads you want.
A semaphore maintains a count which is the number of threads that can access a resource(may be a file or a piece of code) concurrently. This count is also called permit.
Before providing access to the resource to a thread, the semaphore checks if it has any available permits.
If the permits are available, then the thread is granted access else the thread goes to waiting state till some other thread releases the permit.
Semaphore in java
A semaphore in java is represented by an object of java.util.concurrent.Semaphore
class.
You need to define the number of permits or the maximum number of threads that this semaphore will allow to access a resource at the same time(concurrently).
A semaphore is created using new operator as
Semaphore semaphore = new Semaphore(4);
Above statement will allow 4 threads to execute some code at the same time.
Any other threads after 4 threads have got the permit will have to wait till some other threads release the permit.
But, the semaphore will make sure that the desired code is executed by at most 4 threads.
For accessing a shared resource or for getting permit, a thread needs to call semaphore’s acquire
method and for releasing the permit, it needs to call semaphore’s release
method.
Any code enclosed between acquire
and release
will be executed at most by the number of threads that are equal to the number of permits of semaphore.
Javadoc for acquire()
method states
Acquires a permit from this semaphore, blocking until one is available, or the thread is interrupted.
Acquires a permit, if one is available and returns immediately, reducing the number of available permits by one.
If no permit is available then the current thread becomes disabled for thread scheduling purposes.
and Javadoc for release()
method states
Releases a permit, returning it to the semaphore.
Releases a permit, increasing the number of available permits by one. If any threads are trying to acquire a permit, then one is selected and given the permit that was just released.
Following points are worth understanding regarding working of a semaphore
- When a thread gets a permit after calling acquire, the total number of permits is reduced by 1.
- When a thread releases a permit after calling release, the total number of permits is increased by 1.
- If the number of permits is 0, then the thread calling acquire goes to the waiting state.
- If there are more than 1 threads waiting to get the permit and some other thread releases a permit, then there is no guarantee that the thread waiting first will get the permit first.
- If you want that when release is called, the thread waiting first should get the permit first, then create a semaphore object as
Semaphore semaphore = new Semaphore(5, true);
Second argument is called fairness setting. When it is
true
, then a first in first out ordering among the waiting threads is maintained.
Below illustration will also help in grasping the concept of Semaphore.
Semaphore example program
Below is an example program showing the use of semaphore.
import java.util.concurrent.Semaphore; public class SemaphoreExample implements Runnable { Semaphore semaphore = new Semaphore(1); public static void main(String[] args) { // create object of this class SemaphoreExample obj = new SemaphoreExample(); // create two threads Thread t1 = new Thread(obj, "Thread 1"); Thread t2 = new Thread(obj, "Thread 2"); // start threads t1.start(); t2.start(); } public void run() { go(); } public void go() { try { // get permit semaphore.acquire(); System.out.println("Being run by thread - " + Thread.currentThread().getName()); for (int i = 0; i < 5; i++) { System.out.println("Running thread - " + Thread.currentThread().getName()); // halt for 2 seconds Thread.sleep(2000); } // release permit semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } }
Explanation
Above example creates a semaphore with a single permit. This means that only 1 thread can execute the code that is controlled by the semaphore.
The program then creates and starts two threads(1 thread more than the total permits). Each thread executes a method go()
.
Body of go()
method prints the name of current executing thread and loops for 5 times. Inside the loop the thread is made to halt for 2 seconds using Thread.sleep
method to simulate some time taking task and to check if any other thread executes this code at the same time or not.
Note that the entire body of go()
method is enclosed between acquire
and release
methods so that it can executed by at most 1 thread at a time(since total permit count is 1).
Output of the program is
Being run by thread – Thread 1
Running thread – Thread 1
Running thread – Thread 1
Running thread – Thread 1
Running thread – Thread 1
Running thread – Thread 1
Being run by thread – Thread 2
Running thread – Thread 2
Running thread – Thread 2
Running thread – Thread 2
Running thread – Thread 2
Running thread – Thread 2
which shows that the entire go()
method is executed by 1 thread at a time. This also means that the second thread keeps on waiting till the first thread releases the permit.
More on java threads, here.
Now, if you comment out acquire
and release
method calls or increase the number of permits to 2, then the output will be
Being run by thread – Thread 1
Being run by thread – Thread 2
Running thread – Thread 1
Running thread – Thread 2
Running thread – Thread 2
Running thread – Thread 1
Running thread – Thread 1
Running thread – Thread 2
Running thread – Thread 2
Running thread – Thread 1
Running thread – Thread 1
Running thread – Thread 2
which clearly shows that go()
method is executed randomly by both the threads.
Some important points to note about the above semaphore example program are
- If
release
method is not called afteracquire
(try commenting it out), then the second thread will remain in waiting state forever and the program will never stop. - If you call
release
without callingacquire
, then there will be no effect. - If the semaphore is created with 0 or a value less than 0, then no thread will be able to get the permit and the program will never end.
Also check out different ways to halt a thread
Semaphore vs synchronized
A semaphore serves the same purpose as synchronized
block, which is, preventing multiple threads from accessing a shared resource at the same time.
But there is one big difference between both.
A semaphore can be used to limit access to n threads.
That is, with semaphore you can provide shared(or concurrent) access to 3, 4, 5 or n number of threads but synchronized
can be used to grant access to only 1 thread at a time using an object lock.
Hope this post clarified the concept of semaphore in java.