Semaphore in Java


JavaViews 2298

In this tutorial, we will understand Semaphore in Java, its constructors and methods, and lock implementation using detailed examples.

Semaphore in Java

Java Semaphore

Semaphore is one of the techniques that implement thread synchronization. The main use of a semaphore is to control access to a shared resource using a counter variable. Using a semaphore in Java, we can restrict the number of threads that can access the shared resource. In this way, it avoids race conditions. It represents a counter which is a non-negative value that is shared among all the threads. A thread can access the resource if the counter variable is greater than 0 else it denies access. In other words, based on the number of permits that we pass during instantiation, it tracks the number of threads that can access the same.

Working of a semaphore

As we saw in the above section, a semaphore represents a counter variable that we can share among all the threads. It holds a non-negative value which means either 0 or any value greater than 0.

  • First, we initialize the semaphore with the required number of permits.
  • The thread then checks the condition counter>0. If true, it acquires the permission to the shared resource and decrements the counter variable. If false, it blocks the thread and waits for the next permit.
  • After the thread completes the execution of the shared resource, it releases the resource permission and increments the counter.
  • If counter=0, then it denies permission to the shared resource.

The below flow chart will help you understand the working of a semaphore in detail.

Semaphore in Java

Types of Semaphores in Java

There are different types of semaphore in Java:

  • Counting semaphore: It overcomes the problem where more than one process wants to execute a critical section.
  • Bounded semaphore: This contains an upper bound that denotes how many semaphores it can store.
  • Timed semaphore: This allows a thread to execute for a specified time period.
  • Binary semaphore: It is similar to counting semaphore but contains only binary values i.e 0 or 1.

Java Semaphore constructors

A semaphore contains 2 types of constructors as given below:

ConstructorDescription
Semaphore(int permits)It creates a semaphore that initializes the number of permits
Semaphore(int permits, boolean fair)It creates a semaphore that initializes the number of permits along with the fairness parameter

Semaphore methods

MethodDescription
void acquire()Acquires a permit from this semaphore blocking until all are available
void acquire(int permit)Acquires the given number of permits from this semaphore blocking until all are available
void acquireUninterruptibly()Acquires a permit from this semaphore blocking until one is available
void acquireUninterruptibly(int permits)Acquires the given number of permits from this semaphore blocking until all are available
int availablePermits()Returns the number of currently available permits
int drainPermits()Acquires and returns all immediately available permits
int getQueueLength()Returns the number of threads waiting to acquire the permit
boolean hasQueuedThreads()Returns true if there are threads waiting to acquire permit
boolean isFair()Returns true if the semaphore has set the fairness property
void release()Releases the semaphore
void release(int permits)Releases the given number of permits to the semaphore
boolean tryAcquire()Acquires a permit from the semaphore only if one at a time is available
boolean tryAcquire(int permits)Acquires the given number of permits only if all are available
boolean tryAcquire(long timeperiod, TimeUnit unit)Acquires the given number of permits only if one becomes available within the specified time
boolean tryAcquire(int permits, long timeperiod, TimeUnit unit)Acquires the given number of permits only if all are available within the specified time

Java Semaphore example – as a Lock

Below is an example of how we can use a Semaphore as a lock to restrict access to the shared resource. One of the thread classes increments the counter value and the other thread class decrements the counter value. Before accessing the shared resource, the thread acquires the permit using the acquire() method. Once the execution is complete, it releases the permit using the release() method. In this way, it allows other threads to request the permit again. The Counter class contains the shared variable that is count. You might be interested in Concurrency Utilities in Java

import java.util.concurrent.Semaphore;

class Counter {
  static int count = 0;
}

class SemaphoreDemoLock extends Thread {
  
  Semaphore s;
  String name;
  
  SemaphoreDemoLock(Semaphore s, String name){
    this.s = s;
    this.name = name;
  }
  
  public void run() {
    if(this.getName().equals("Thread 1")) {
      System.out.println(name + " started execution");
      
      try {
        System.out.println(name + " waiting to acquire permit");
        s.acquire();
        System.out.println(name + " acquired permit");
        
        for(int i=0;i<3;i++) {
          Counter.count++;
          System.out.println(name + ":" + Counter.count);
          Thread.sleep(1000);
        }
      }
      catch(InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(name + " releasing permit");
      s.release();
    }
    else {
      System.out.println(name + " started execution");
      
      try {
        System.out.println(name + " waiting for permit");
        s.acquire();
        System.out.println(name + " acquired permit");
        
        for(int i=0;i<3;i++) {
          Counter.count--;
          System.out.println(name + ":" + Counter.count);
          Thread.sleep(1000);
        }
      }
      catch(InterruptedException e) {
        e.printStackTrace();
      }
      
      System.out.println(name + " releasing permit");
      s.release();
    }
  }

}


public class SemaphoreDemo {
  public static void main(String[] args) throws InterruptedException {
    Semaphore s = new Semaphore(1);
    
    SemaphoreDemoLock sd1 = new SemaphoreDemoLock(s, "Thread 1");
    SemaphoreDemoLock sd2 = new SemaphoreDemoLock(s, "Thread 2");
    
    sd1.start();
    sd2.start();
    
    
    sd1.join();
    sd2.join();
    
    System.out.println("Counter value: " + Counter.count);
  }
}
Thread 2 started execution
Thread 2 waiting for permit
Thread 1 started execution
Thread 1 waiting for permit
Thread 2 acquired permit
Thread 2:-1
Thread 2:-2
Thread 2:-3
Thread 2 releasing permit
Thread 1 acquired permit
Thread 1:-4
Thread 1:-5
Thread 1:-6
Thread 1 releasing permit
Counter value: -6

Semaphore Example

In this example, we can see how to create a Semaphore with a specified number of permits. We create a Semaphore constructor with 3 permits. We can check the available number of permits using the availablePermits()method. A thread can acquire the permit using the acquire() method and release it using the release() method. We can clearly understand the lock synchronization by creating 2 different threads.

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
  
  static Semaphore s = new Semaphore(3);
  
  static class SampleThread extends Thread {
    String name = "";
    SampleThread(String name){
      this.name = name;
    }
    
    public void run() {
      try {
        System.out.println("Available number of permits for " + name + " is: " + s.availablePermits());
        System.out.println(name + " waiting to acquire lock");
        
        s.acquire();
        System.out.println(name + " acquired permit");
        
        try {
          for(int i=0;i<3;i++) {
            System.out.println(name + " executing " + ":" + " Available number of permits: " + s.availablePermits());
            Thread.sleep(1000);
          }
        }
        finally {
          System.out.println(name + " releasing permit");
          s.release();
          System.out.println("Available number of permits for " + name + " is: " + s.availablePermits());
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      
    }
  }

  public static void main(String[] args) throws InterruptedException {
    
    System.out.println("Total number of available permits: " + s.availablePermits());
    SampleThread st1 = new SampleThread("Thread 1");
    
    st1.start();
    
    SampleThread st2 = new SampleThread("Thread 2");
    
    st2.start();
    
  }

}
Total number of available permits: 3
Available number of permits for Thread 1 is: 3
Available number of permits for Thread 2 is: 3
Thread 1 waiting to acquire lock
Thread 2 waiting to acquire lock
Thread 1 acquired permit
Thread 1 executing : Available number of permits: 2
Thread 2 acquired permit
Thread 2 executing : Available number of permits: 1
Thread 1 executing : Available number of permits: 1
Thread 2 executing : Available number of permits: 1
Thread 2 executing : Available number of permits: 1
Thread 1 executing : Available number of permits: 1
Thread 1 releasing permit
Thread 2 releasing permit
Available number of permits for Thread 1 is: 2
Available number of permits for Thread 2 is: 3

You might be interested in reading an article on Multithreading in Java

Reference Reference2

Translate ยป