March 12, 2024

Semaphore in Java With Examples

Semaphore is a synchronization aid that can be used for inter-communication among threads or to restrict access to resource. An implementation of Semaphore in Java is available for out of box use and it is part of java.util.concurrent package.

Counting semaphore

The implementation of semaphore readily available in the Java concurrency package is a counting semaphore.

Conceptually, a semaphore maintains a set of permits. When a semaphore is created it is created with a given number of permits. The working of semaphore in Java can be explained using the following steps-

  1. A thread that wants to access a shared resource tries to acquire a permit using the acquire() method.
  2. If permit is available or in other words if semaphore count is greater than zero then the thread acquires a permit otherwise thread is blocked.
  3. With every successful acquisition of the permit the count is also decremented. If count becomes zero then no permit can be given.
  4. When the thread is done with the shared resource it can release the acquired permit using the release() method. This increments the semaphore's count.
  5. Any blocking thread waiting to acquire a permit can get a permit once count is more than zero.

Java semaphore constructors

  • Semaphore(int permits)- Creates a Semaphore with the given number of permits and nonfair fairness setting.
  • Semaphore(int permits, boolean fair)- Creates a Semaphore with the given number of permits and the given fairness setting.

Semaphore example in Java

Let’s say there is a method that is computation heavy and you want to restrict access to this method to 2 threads at any given time. In this scenario you can use Semaphore created with 2 permits.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
  public static void main(String[] args) {
    // Semaphore with 2 permits
    Semaphore s = new Semaphore(2);
    ExecutorService ex = Executors.newFixedThreadPool(4);
    // Executing 6 times with a pool of 4 threads
    for(int i = 0; i < 6; i++) {
      ex.execute(new HeavyDuty(s));
    }
    ex.shutdown();
  }
}

class HeavyDuty implements Runnable{
  private Semaphore s;
  HeavyDuty(Semaphore s){
    this.s = s;
  }
  @Override
  public void run() {
    try {
      s.acquire();
      System.out.println("Permit ACQUIRED by " + Thread.currentThread().getName());
      doProcessing();	
      System.out.println("Permit released by " + Thread.currentThread().getName());
      s.release();		
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }				
  }
  private void doProcessing() throws InterruptedException{
    System.out.println("doing heavy computation processing ");
    Thread.sleep(5000);
  }
}
Output
Permit ACQUIRED by pool-1-thread-1
doing heavy computation processing 
Permit ACQUIRED by pool-1-thread-2
doing heavy computation processing 
Permit released by pool-1-thread-1
Permit ACQUIRED by pool-1-thread-4
doing heavy computation processing 
Permit released by pool-1-thread-2
Permit ACQUIRED by pool-1-thread-3
doing heavy computation processing 
Permit released by pool-1-thread-4
Permit ACQUIRED by pool-1-thread-1
doing heavy computation processing 
Permit released by pool-1-thread-3
Permit ACQUIRED by pool-1-thread-2
doing heavy computation processing 
Permit released by pool-1-thread-1
Permit released by pool-1-thread-2

As you can see at any given time permits are acquired by 2 threads.

Binary Semaphore

A semaphore in Java created with only one permit can serve as a mutual exclusion lock. This is more commonly known as a binary semaphore, because it only has two states: one permit available, or zero permits available.

Binary Semaphore example in Java

Here is a simple binary semaphore example where a shared counter is used among multiple threads. Binary semaphore let only one thread access the shared resource at any given time.

public class SemaphoreDemo {
  public static void main(String[] args) {
    // Semaphore with 1 permit
    Semaphore s = new Semaphore(1);
    SharedCounter counter = new SharedCounter(s);
    for(int i = 0; i < 6; i++) {
      new Thread(counter).start();
    }
  }
}

class SharedCounter implements Runnable{
  private int c = 0;
  private Semaphore s;
  SharedCounter(Semaphore s){
   this.s = s;
  }
  @Override
  public void run() {
    try {
      s.acquire();
      incrCounter();
      s.release();	
    }catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  // incrementing the value
  public void incrCounter()  throws InterruptedException{
    Thread.sleep(10);
    System.out.println("Value for Thread After increment - " + 
        Thread.currentThread().getName() + " " + ++c);
  }
}
Output
Value for Thread After increment - Thread-0 1
Value for Thread After increment - Thread-1 2
Value for Thread After increment - Thread-2 3
Value for Thread After increment - Thread-3 4
Value for Thread After increment - Thread-4 5
Value for Thread After increment - Thread-5 6

To see how threads can interfere you can comment the acquire and release methods with in the run() method.

public void run() {
  try {
    //s.acquire();
    incrCounter();
    //s.release();	
  }catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
}

Running after commenting gives the following output for one run where same count is displayed for 2 threads.

Value for Thread After increment - Thread-4 1
Value for Thread After increment - Thread-2 2
Value for Thread After increment - Thread-0 3
Value for Thread After increment - Thread-5 4
Value for Thread After increment - Thread-3 1
Value for Thread After increment - Thread-1 2

Methods in Java Semaphore class

Some of the important methods in Semaphore class in Java are as follows-

  1. acquire()- Acquires a permit from this semaphore, blocking until one is available, or the thread is interrupted.
  2. acquire(int permits)- Acquires the given number of permits from this semaphore, blocking until all are available, or the thread is interrupted.
  3. availablePermits()- Returns the current number of permits available in this semaphore.
  4. drainPermits()- Acquires and returns all permits that are immediately available, or if negative permits are available, releases them.
  5. getQueuedThreads()- Returns a collection containing threads that may be waiting to acquire.
  6. isFair()- Returns true if this semaphore has fairness set true.
  7. release()- Releases a permit, returning it to the semaphore.
  8. tryAcquire()- Acquires a permit from this semaphore, only if one is available at the time of invocation.
  9. tryAcquire(int permits)- Acquires the given number of permits from this semaphore, only if all are available at the time of invocation.

That's all for the topic Semaphore in Java With Examples. If something is missing or you have something to share about the topic please write a comment.


You may also like

No comments:

Post a Comment