Open In App

Using Semaphore to Protect More than One Copy of a Resource in Java

Last Updated : 04 Aug, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

A semaphore work on the concepts of permits. A semaphore is initialized with a certain number of permits. The number of permits is equal to the number of shared resources available. When a thread wants to access a shared resource, it acquires the permit and when done accessing the shared resource, it releases the permit.

Illustration:

Illustration 1: Restaurant application having 10 tables.

There are 20 people standing outside the restaurant, waiting to enter. In this case, the number of semaphores is the same as the number of resources(tables) which is 10. For any customer to enter the restaurant, he has to acquire a permit. After acquiring the permit, he chooses one of the available tables. Once his order is completed he releases the semaphore making it available for other customers waiting in the queue. Thus semaphore ensures that at a time only 10 customers can enter the restaurant and make orders.

Illustration 2: People waiting in a queue at an ATM center. 

If there are 2 ATM machines, at a time only 2 people can access the ATM machine and withdraw money. When a customer enters the ATM center, he acquires a permit if available, then checks which of the ATM machines is free for use. Once he gets an available ATM machine, he locks it and then enters the PIN and withdraws money. After withdrawing, he releases the lock on the ATM machine and also releases the semaphore. The reason he has to lock the ATM machine before withdrawing money is if 2 people after acquiring the semaphore end up in the same ATM machine. Here the ATM machine is a shared resource and the number of semaphores is equal to the number of ATM machines.

Working of a semaphore is as follows:

For our restaurant application, we initialize our semaphore with 10 permits which are equal to the number of tables available. If there are 30 customers in the queue waiting to enter the restaurant, Customer 1 acquires a permit, enters the restaurant. When a customer enters the restaurant, he acquires a permit and chooses 1 out of 10 available tables. Let’s suppose that customer #1 chooses table # 5. The number of available permits is 9. Customer # 2 acquires another permit since permits are still available and chooses table # 7. In this way, 10 customers can acquire permits and choose the unoccupied tables. When customer # 11 enters the restaurant, he is blocked until one of the 10 customers releases the semaphore and leaves the table.

Example:

Java




// Java Program to Use a Semaphore to Protect more than One
// Copy of a Resource
 
// Importing input output classes
import java.io.*;
// Importing utility classes
import java.util.Arrays;
import java.util.concurrent.Semaphore;
 
// Class 1
// Helper class 1
class ATMQueue {
 
    // Member variables
    private Semaphore semaphore;
    private boolean[] freeAtms;
 
    // Method 1
    public ATMQueue()
    {
 
        semaphore = new Semaphore(2);
        freeAtms = new boolean[2];
        Arrays.fill(freeAtms, true);
    }
 
    // Method 2
    public void withDrawMoney()
    {
 
        // Try block to check for exceptions
        try {
 
            // Try to acquire a semaphore.
            // If none are available, thread will block here
            // till a semaphore becomes available
            semaphore.acquire();
 
            // Check for available ATM machine
            int atmMachine = getAvailableATM();
 
            // since atm Machine is available to withdraw
            // money, we acquire a semaphore
            System.out.println(
                Thread.currentThread().getName()
                + ":-Withdrawing money from atm number :-"
                + atmMachine);
 
            System.out.println(
                "---------------------------------");
 
            Thread.sleep((long)(Math.random() * 1000));
 
            System.out.println(
                Thread.currentThread().getName()
                + ":-done withdrawing money");
 
            releaseATM(atmMachine);
 
            System.out.printf(
                "ATM machine :- %s is now available",
                atmMachine);
 
            System.out.println(
                "---------------------------------");
 
            System.out.println(
                "About to release the semaphore");
 
            semaphore.release();
 
            System.out.println("Semaphore released");
        }
 
        // catch block to handle the exceptions
        catch (InterruptedException e) {
 
            // Print the exceptions along with line number
            e.printStackTrace();
        }
    }
 
    // Method 3
    private void releaseATM(int atmNumber)
    {
 
        // We are making specific atm machine free for use
        synchronized (freeAtms)
        {
            freeAtms[atmNumber] = true;
        }
    }
 
    // Method 4
    private int getAvailableATM()
    {
        int freeAtm = -1;
 
        // We are using synchronized to ensure that only 1
        // thread can access and modify the shared boolean
        // array freeAtms
        synchronized (freeAtms)
        {
 
            for (int i = 0; i < freeAtms.length; i++) {
 
                if (freeAtms[i]) {
                    freeAtms[i] = false;
                    freeAtm = i;
 
                    break;
                }
            }
        }
 
        return freeAtm;
    }
}
 
// Class 2
// Helper class 2
class WithdrawMoneyTask implements Runnable {
 
    private ATMQueue atmQueue;
 
    public WithdrawMoneyTask(ATMQueue atmQueue)
    {
        // TODO Auto-generated constructor stub
        this.atmQueue = atmQueue;
    }
 
    @Override public void run()
    {
        System.out.println(
            Thread.currentThread().getName()
            + ": - about to withdraw money after acquiring the permit");
 
        atmQueue.withDrawMoney();
    }
}
 
// Class 3
// Main class
class GFG {
 
    // Main  driver method
    public static void main(String[] args)
    {
 
        // Print statement
        System.out.println("GFG!");
 
        // Creating an object of class 1 in main() method
        ATMQueue atmQueue = new ATMQueue();
 
        // Creating Thread class object
        Thread thread[] = new Thread[10];
 
        for (int i = 0; i < 10; i++) {
            thread[i] = new Thread(
                new WithdrawMoneyTask(atmQueue),
                "Thread " + i);
        }
 
        for (int i = 0; i < 10; i++) {
            thread[i].start();
        }
    }
}


 
 

Java




// Java Program to Use a Semaphore to Protect more than One
// Copy of a Resource
 
// Importing input output classes
import java.io.*;
// Importing utility classes
import java.util.Arrays;
import java.util.concurrent.Semaphore;
 
// Class 1
// Helper class 1
class ATMQueue {
 
    // Member variables
    private Semaphore semaphore;
    private boolean[] freeAtms;
 
    // Method 1
    public ATMQueue()
    {
 
        semaphore = new Semaphore(2);
        freeAtms = new boolean[2];
        Arrays.fill(freeAtms, true);
    }
 
    // Method 2
    public void withDrawMoney()
    {
 
        // Try block to check for exceptions
        try {
 
            // Try to acquire a semaphore.
            // If none are available, thread will block here
            // till a semaphore becomes available
            semaphore.acquire();
 
            // Check for available ATM machine
            int atmMachine = getAvailableATM();
 
            // since atm Machine is available to withdraw
            // money, we acquire a semaphore
            System.out.println(
                Thread.currentThread().getName()
                + ":-Withdrawing money from atm number :-"
                + atmMachine);
 
            System.out.println(
                "---------------------------------");
 
            Thread.sleep((long)(Math.random() * 1000));
 
            System.out.println(
                Thread.currentThread().getName()
                + ":-done withdrawing money");
 
            releaseATM(atmMachine);
 
            System.out.printf(
                "ATM machine :- %s is now available",
                atmMachine);
 
            System.out.println(
                "---------------------------------");
 
            System.out.println(
                "About to release the semaphore");
 
            semaphore.release();
 
            System.out.println("Semaphore released");
        }
 
        // catch block to handle the exceptions
        catch (InterruptedException e) {
 
            // Print the exceptions along with line number
            e.printStackTrace();
        }
    }
 
    // Method 3
    private void releaseATM(int atmNumber)
    {
 
        // We are making specific atm machine free for use
        synchronized (freeAtms)
        {
            freeAtms[atmNumber] = true;
        }
    }
 
    // Method 4
    private int getAvailableATM()
    {
        int freeAtm = -1;
 
        // We are using synchronized to ensure that only 1
        // thread can access and modify the shared boolean
        // array freeAtms
        synchronized (freeAtms)
        {
 
            for (int i = 0; i < freeAtms.length; i++) {
 
                if (freeAtms[i]) {
                    freeAtms[i] = false;
                    freeAtm = i;
 
                    break;
                }
            }
        }
 
        return freeAtm;
    }
}
 
// Class 2
// Helper class 2
class WithdrawMoneyTask implements Runnable {
 
    private ATMQueue atmQueue;
 
    public WithdrawMoneyTask(ATMQueue atmQueue)
    {
        // TODO Auto-generated constructor stub
        this.atmQueue = atmQueue;
    }
 
    @Override public void run()
    {
        System.out.println(
            Thread.currentThread().getName()
            + ": - about to withdraw money after acquiring the permit");
 
        atmQueue.withDrawMoney();
    }
}
 
// Class 3
// Main class
class GFG {
 
    // Main  driver method
    public static void main(String[] args)
    {
 
        // Print statement
        System.out.println("GFG!");
 
        // Creating an object of class 1 in main() method
        ATMQueue atmQueue = new ATMQueue();
 
        // Creating Thread class object
        Thread thread[] = new Thread[10];
 
        for (int i = 0; i < 10; i++) {
            thread[i] = new Thread(
                new WithdrawMoneyTask(atmQueue),
                "Thread " + i);
        }
 
        for (int i = 0; i < 10; i++) {
            thread[i].start();
        }
    }
}


Output

GFG!
Thread 7: - about to withdraw money after acquiring the permit
Thread 4: - about to withdraw money after acquiring the permit
Thread 9: - about to withdraw money after acquiring the permit
Thread 2: - about to withdraw money after acquiring the permit
Thread 8: - about to withdraw money after acquiring the permit
Thread 0: - about to withdraw money after acquiring the permit
Thread 1: - about to withdraw money after acquiring the permit
Thread 5: - about to withdraw money after acquiring the permit
Thread 3: - about to withdraw money after acquiring the permit
Thread 6: - about to withdraw money after acquiring the permit
Thread 4:-Withdrawing money from atm number :-1
---------------------------------
Thread 7:-Withdrawing money from atm number :-0
---------------------------------
Thread 7:-done withdrawing money
ATM machine :- 0 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 9:-Withdrawing money from atm number :-0
---------------------------------
Thread 4:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 2:-Withdrawing money from atm number :-1
---------------------------------
Thread 9:-done withdrawing money
ATM machine :- 0 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 8:-Withdrawing money from atm number :-0
---------------------------------
Thread 2:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 0:-Withdrawing money from atm number :-1
---------------------------------
Thread 0:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 1:-Withdrawing money from atm number :-1
---------------------------------
Thread 1:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 5:-Withdrawing money from atm number :-1
---------------------------------
Thread 5:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 3:-Withdrawing money from atm number :-1
---------------------------------
Thread 3:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 6:-Withdrawing money from atm number :-1
---------------------------------
Thread 8:-done withdrawing money
ATM machine :- 0 is now available---------------------------------
About to release the semaphore
Semaphore released
Thread 6:-done withdrawing money
ATM machine :- 1 is now available---------------------------------
About to release the semaphore
Semaphore released

Also, the real-time output is appended due to the occurrence of semaphore leading to concurrency in the execution of our program.

 Output explanation:

  • In this code example, there are 3 classes. WithdrawMoneyTask class defines the job/task to be executed.  So it implements the Runnable interface.
  • It has a private member ATMQueue which represents an ATM machine. We call it’s withdrawMoney() method.
  • In ATMQueue class, we have 2 member variables. The member variable semaphore is defined in the constructor. It is initialized to 2 because there are only 2 ATMs. The member variable freeAtms is a boolean array which is initialized to true in the constructor, indicating both the atm machines are free initially.
  • In withdrawMoney(), we first acquire the semaphore, and then check which atm machine is free. This is done through getAvailableATM(). There is a possibility that 2 threads may acquire the semaphore and enter this block of code and modify the shared resource variable freeAtms. So the code to check for available ATMs and mark it as busy is in the synchronized block.
  • Once we find the available ATM machine, we withdrawMoney. Once the money is withdrawn, we release the ATM through releaseATM(atmNumber) method. This code is also in synchronized, to ensure that only 1 thread enters this block and modifies freeAtms boolean array. Then we release the semaphore for other waiting threads to acquire and withdrawMoney based on the available ATM machine.

As per the output, at any given point in time, only 2 threads can acquire the semaphore and withdraw money. If a thread is unable to acquire a semaphore, it is blocked until one of the threads which has acquired the semaphore releases it. This is how semaphores can be used to protect more than one copy of a shared resource which in this case is an ATM machine.

 



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads