Mutex and Semaphores

Mutexes are used to control access to a section of code that cannot be executed concurrently by more than one thread. Simplifying the story, mutex are semaphore with 1 and only 1 permit to access code simultaneously (mutex == binary semaphore)

The following code simulates a small shop with 1 cashier only. The customer waiting to pay their groceries have to wait in the lane and they can start paying when the preceeding customer has finished to pay the own goods. Code is self-explanatory as I have added many comments.

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package education.jtrainer.javainterviewquestions;

import java.util.concurrent.Semaphore;
import java.util.Random;
/**
 *
 * @author Diego Gabriele
 * 
 * This example is related to a bunch of people waiting in a  grocery lane to check out
 * For the example purpose there are 6 people arriving in the lane in a random time interval between 0 and 10 seconds.
 */
public class MutexTutorial {
    

    // max 1 people for 1 cashier
    static Semaphore semaphore = new Semaphore(1);

    static class MyBuyer extends Thread {

        String name = "";

        MyBuyer(String name) {
            this.name = name;
        }

        public void run() {

            try {

                System.out.println(name + " : trying to pay groceries...");
                System.out.println(name + " : available cashiers: " + semaphore.availablePermits());

                semaphore.acquire();
                System.out.println(name + " : got the permit to pay my goods!");

                try {
                    for (int i = 1; i <= 5; i++) {
                        System.out.println(name + " : is paying good n. " + i + ", available cashiers : "+ semaphore.availablePermits());
                        // assuming it takes 5 sec to pay 1 good
                        Thread.sleep(5000);

                    }

                } finally {

                    // calling release() after succesfully paying grocery
                    System.out.println(name + " : releasing lock...");
                    semaphore.release();
                    System.out.println(name + " : available cashiers now: " + semaphore.availablePermits());
                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

    public static void main(String[] args) throws InterruptedException {


                System.out.println("This is the Mutex tutorial");
                
                MyBuyer t1 = new MyBuyer("Anna");
                t1.start();

                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t2 = new MyBuyer("Bob");
        t2.start();

                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t3 = new MyBuyer("Charlie");
        t3.start();

                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t4 = new MyBuyer("Don");
        t4.start();

                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t5 = new MyBuyer("Eric");
        t5.start();

                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t6 = new MyBuyer("Fred");
        t6.start();

    }
}

 

Semaphores have got n permit so they restrict the number of simultaneous users of a shared resource up to a maximum number. Threads can request access to the resource (decrementing the semaphore), and can signal that they have finished using the resource (incrementing the semaphore)”.

The following code simulates a bigger shop with 2 cashiers. The customers waiting to pay their groceries have to wait in the lane and they can start paying when the preceeding customer has finished to pay the own goods.

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package education.jtrainer.javainterviewquestions;

import java.util.concurrent.Semaphore;
import java.util.Random;
/**
 *
 * @author Diego Gabriele
 */
public class SemaphoreTutorial{
    
    static Semaphore semaphore=new Semaphore(2);
    
    static class MyBuyer extends Thread {
                
        String name = "";

        MyBuyer(String name) {
            this.name = name;
        }

        public void run() {

            try {
                System.out.println(name + ": trying to pay goods...");
                                 System.out.println(name + ": available cashiers: " + semaphore.availablePermits());
                System.out.println(name + ": there are "+semaphore.getQueueLength() + " people in the lane waiting to pay their goods who precede me");
                                
                                semaphore.acquire();
                                System.out.println(name + ": got the permit to pay my groceries");
                                    try {
                    for (int i = 1; i <= 5; i++) {
                        System.out.println(name + " : I am paying good n." + i + ", available cashiers : "+ semaphore.availablePermits());
                        // sleep 5000 ms - this is the time needded to pay 
                        Thread.sleep(5000);

                    }

                } finally {
                    // calling release() after succesfully paying the groceries
                    System.out.println(name + ": releasing lock..., cashier is available now to serve a new customer");
                    semaphore.release();
                    System.out.println(name + ": available cashiers: " + semaphore.availablePermits());
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        System.out.println("This is the Semaphore tutorial");
                
                MyBuyer t1 = new MyBuyer("Anna");
                t1.start();
                
                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t2 = new MyBuyer("Bob");
        t2.start();

                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t3 = new MyBuyer("Charlie");
        t3.start();
                
                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t4 = new MyBuyer("Don");
        t4.start();
                
                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t5 = new MyBuyer("Eric");
        t5.start();

                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t6 = new MyBuyer("Fred");
        t6.start();
    }
}

 

A little bit efficient way to manage the buyers queue and to offer a better customer service is to allocate new resources (cashiers) to serve the buyers. In this way the waiting time in the lane is reduces.

In our code, when in the lane there are many people (>=2) a new cashier is allocated so buyers can pay their groceries.

package education.jtrainer.javainterviewquestions;

import java.util.concurrent.Semaphore;
import java.util.Random;
/**
 *
 * @author Diego Gabriele
 */
public class ResizableSemaphore extends Semaphore{
    
    int originalPermits;
    
    public ResizableSemaphore(int permits) {
        super(permits);
        originalPermits=permits;
    }
    
    @Override
    protected void reducePermits(int reduction) {
        super.reducePermits(reduction);
    }
    
    public int usedPermits() {
        return originalPermits - availablePermits();
    }
    @Override
    public void release(int i){
        super.release(i);
    }
    
    static class MyBuyer extends Thread {

                ResizableSemaphore semaphore;            
        String name = "";
                int totalCashiers; //this is the number of the total number of cashiers allocated by the shop manager

        MyBuyer(String name, ResizableSemaphore semaphore) {
            this.name = name;
                        this.semaphore=semaphore;
        }

        public void run() {

            try {
                totalCashiers=semaphore.usedPermits()+semaphore.availablePermits();
                                System.out.println(name + ": trying to pay groceries...");
                                System.out.println(name + ": available cashiers: " + semaphore.availablePermits()+" total cashiers: "+totalCashiers);
                System.out.println(name + ": there are "+semaphore.getQueueLength() + " people in the lane waiting to pay their goods who precede me");
                                if (semaphore.getQueueLength() >=2) {
                                    System.out.println("too many people in the lane, manager will allocate  another cashier");
                                    semaphore.release(1);
                                    System.out.println("Available cashiers are now: " + semaphore.availablePermits());
                                }
                                
                                semaphore.acquire();
                                System.out.println(name + " :got the permit to pay goods!");
                                try {
                    for (int i = 1; i <= 5; i++) {
                                                totalCashiers=semaphore.usedPermits()+semaphore.availablePermits();
                        System.out.println(name + ": I am paying good n." + i + ", available cashiers: "+ semaphore.availablePermits() +" total cashiers: "+totalCashiers);
                        // sleep 5000 ms - this is the time needded to pay 
                        Thread.sleep(5000);
                    }

                } finally {
                    // calling release() after succesfully paying the groceries
                    System.out.println(name + ": releasing lock..., cashier is available now to serve a new customer");
                    semaphore.release();
                    System.out.println(name + ": available cashiers now: " + semaphore.availablePermits());
                                        
                                        // when the queue length get reduces the manager deallocates the cashier
                                        if (semaphore.getQueueLength() <=1 && semaphore.availablePermits()>2 ){
                                            semaphore.reducePermits(1);
                                            System.out.println("manager removed cashier");
                                            System.out.println(name + ": available cashiers: " + semaphore.availablePermits());
                                        }
                                
                                
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        System.out.println("This is the ResizableSemaphore tutorial");
                
                ResizableSemaphore semaphore=new ResizableSemaphore(2);
                
                MyBuyer t1 = new MyBuyer("Anna",semaphore);
                t1.start();
                
                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t2 = new MyBuyer("Bob",semaphore);
        t2.start();

                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t3 = new MyBuyer("Charlie",semaphore);
        t3.start();
                
                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t4 = new MyBuyer("Don",semaphore);
        t4.start();
                
                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t5 = new MyBuyer("Eric",semaphore);
        t5.start();

                Thread.sleep(new Random().nextInt(10000));
        MyBuyer t6 = new MyBuyer("Fred",semaphore);
        t6.start();
    }
}

 

The most important difference between the semaphore and the mutex is the concept of “ownership”. There is no requirement that a thread that releases a permit must have acquired that permit by calling acquire(). Correct usage of a semaphore is established by programming convention in the application. This means semaphores have no notion of ownership, i.e. any thread can release a semaphore.. Whereas a mutex does have the concept of ownership (i.e. you can only release a mutex you have acquired).

Code tested with jdk 1.8.0_151.

References

[1] – https://crunchify.com/what-is-java-semaphore-and-mutex-java-concurrency-multithread-explained-with-example/

[2] – https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html

[3] – https://stackoverflow.com/questions/771347/what-is-mutex-and-semaphore-in-java-what-is-the-main-difference

Leave a Reply

Your email address will not be published. Required fields are marked *