Understanding Java Semaphore Concurrency | JavaStructures

Understanding Java Semaphore Concurrency


Semaphore is a variable or abstract data type that controls the number of threads than can access some shared resource. This access to shared resource is controlled using permits.

Semaphore - Table Of Contents

Understanding Java Semaphore Java Semaphore - Print even and odd numbers using Threads Java Semaphore - Print numbers sequence using three Threads

What is a semaphore?

  • Semaphore is a term derived from ancient Greek words (sêma, "sign") and (-phoros, "bearing, bearer") meaning 'sign, bearing/bearer'.
    Semaphore Tutorial
  • Semaphore is technique used to control access to a shared resource.
  • It maintains a counter which specifies the number of resources available.
  • When there is a request to access the process, the access is allowed only if the counter value is greater than zero. If the counter value is zero it indicates thatthe resource is not available and so cannot be accessed.
  • So Semaphore acts as a doorkeeper who gaurds the shared resource. It grants access only if counter is available.

How does semaphore work?

  • A semaphore acts as a limiter of available resource pool depth; for example, a semaphore with a capacity of 10 allows a maximum of 10 threads to acquire it at once, and any further threads that attempt to acquire it will block until one of the other threads releases it.
  • This is somewhat different from ordinary mutual exclusion or monitor locking, which is typically used to prevent more than one thread from simultaneously modifying the same variables and causing inconsistent results or program state.
  • So how does semaphore specify how many threads can simultaneously access it. This is specified using a permit.
Consider permit as a sort of token. Suppose you are going to a restaurant having 3 tables for dining. Each costumer who comes is given a token and only then he can occupy the table. After he has finished eating the token needs to be returned. Each token represents a table. If a customer arrives at the restaurant for dinner and all 3 tables are occupied, he will not be given a token and put in a waiting list. He will need to wait for an existing customer to finish dining and return the token so that it can be provided to waiting customer.

Java Semaphore Permit
If the permit value is greater than zero, semaphore allows a thread access to the shared resource.
If the permit value is zero or less than zero, the thread is put in waiting list. So in order to access the shared resource, the thread will need to be granted a permit from the semaphore.

Java Semaphore Tutorial
Semaphore has the following constructors -
  • Semaphore(int permits)
  • Semaphore(int permits, boolean fair)
In the constructor we specify the initial value for the permits. This initial value can be negative, positive or zero. In case of zero or negative value, an existing thread must release it. This release will increment the value of semaphore permit. If permit value becomes more than zero then a thread from the waiting list is granted access to the shared resource and the permit value will be decremented. The fair value in constructor ensures that threads in the waiting list are granted a permit in the order in which they requested access.
The Semaphore's provides following acquire methods-
  • void acquire( ) throws InterruptedException
    This method checks the semaphore permit value. If it is greater than zero it acquires the permit and decrements the number of available permits by 1. If the semaphore value is zero or negative the thread is put in waiting list and stays there until some other thread calls release() method on this semaphore or some other thread interrupts the current thread.
  • void acquire(int permits) throws InterruptedException
    This acquire method checks if the Semaphore permit value is greater than or equal to the permits provided as argument.If available and decrements the number of available permits by permits. If not available the threading requesting acquire is put in waiting list and stays there until some other thread calls release() method on this semaphore or some other thread interrupts the current thread.
  • void release( )
    This Semaphore method releases permit to the shared resource and increases the number of available permits by 1. In order to release lock to the shared resource by calling the release() method it is not mandatory that the thread must have previously acquired permit by calling the acquire method.
  • void release(int permits )
    This Semaphore method releases permits as specified in the method argument and increases the number of available permits by method argument permits. In order to release lock to the shared resource by calling the release(permits) method it is not mandatory that the thread must have previously acquired permit by calling the acquire method.

Semaphore Access Tutorial
package com.javastructures;

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
	public static void main(String args[]) {

		//create a semaphore with single permit
		Semaphore semaphore = new Semaphore(1);
		System.out.println("Created Semaphore with 1 permit");

        //create a task which uses the above created semaphore 
		Task1 task1 = new Task1(semaphore);
		Thread t1 = new Thread(task1, "Task1");
		t1.start();

        //create a task which uses the above created semaphore 
		Task2 task2 = new Task2(semaphore);
		Thread t2 = new Thread(task2, "Task2");
		t2.start();
	}

}

class Task1 implements Runnable {
	Semaphore semaphore;

	public Task1(Semaphore semaphore) {
		this.semaphore = semaphore;
	}

	@Override
	public void run() {
		try {
			System.out.println(Thread.currentThread().getName() + " is waiting to acquire the permit");
			//acquire a permit if available else wait for one to be available
			semaphore.acquire();
			System.out.println(Thread.currentThread().getName() + " has acquired the permit");

			for (int i = 0; i < 5; i++) {
				System.out.println(i);
				Thread.sleep(500);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " has released the permit");
        //release the acquired permit  
		semaphore.release();
	}
}

class Task2 implements Runnable {
	Semaphore semaphore;

	public Task2(Semaphore semaphore) {
		this.semaphore = semaphore;
	}

	@Override
	public void run() {
		try {
			System.out.println(Thread.currentThread().getName() + " is waiting to acquire the permit");
			//acquire a permit if available else wait for one to be available
			semaphore.acquire();
			System.out.println(Thread.currentThread().getName() + " has acquired the permit");

			for (int i = 0; i < 5; i++) {
				System.out.println(i);
				Thread.sleep(500);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName() + " has released the permit");
        //release the acquired permit
		semaphore.release();
	}
}
Run the program as a Java Application -
Java Semaphore Program