The Java Multi Threading - Deadlock and Inter Thread Communication


The deadlocks are real headache and only proper programming is solution to avoid it, Inter thread communication helps so much, wait, notify and notifyall are the big solution for proper working of threads, learn how to control your threads.

This is the final part of Thread concept of Java. In this part we will discuss about some of the very complex known concepts which could be a big problem if you are trying to implement some kind of banking concepts. When you are working on the threads, you are breaking down your program into smaller modules which must work in co-ordinance to make the application a success. But in some cases if the threads are not managed properly.

Deadlock

Deadlock is a special condition where two thread units are trying to access some resource while the resource is undertaken by the lock of another thread. For example consider two different threads which are using some resources of the program. Now consider our Jhon and Kenny making coffee. Both has access to one of the resources, Milk. But it so happens that Jhon has Coffee powder Bru and Kenny has Nescafe, however Jhon likes Nescafe and Kenny wants Bru. So aftre accessing the milk they are trying to access the coffee powder from each other, but No body is ready to give their resource first. So this condition is known as Deadlock.

deadlock

Now Let us try to make a program which shows this scenario.


class MakeColdCoffee{
    String name;
    
    MakeColdCoffee(String name){
        this.name = name;
    }
    
    public synchronized void accessMilk(MakeColdCoffee ct){
        System.out.println("Milk Accessed by "+Thread.currentThread().getName());
        System.out.println("now trying to access Coffee Powder form other");
        ct.accessCoffeePowder();
    }
    
    public synchronized void accessCoffeePowder(){
        System.out.println("Accessing Coffee Powder");
    }
}

class StartMakingCoffee{
    public static void main(String[] args){
        final MakeColdCoffee jhon = new MakeColdCoffee("Jhon");
        final MakeColdCoffee kenny = new MakeColdCoffee("Kenny");
        new Thread(new Runnable(){
            public void run(){ jhon.accessMilk(kenny); }
        },"jhon").start();
        new Thread(new Runnable(){
            public void run(){ kenny.accessMilk(jhon); }
        },"kenny").start();

    }
}

when you run you get this output

Milk Accessed by jhon
now trying to access Coffee Powder form other
Milk Accessed by kenny
now trying to access Coffee Powder form other

In this example we created two objects named Jhon and Kenny which are trying to access the methods accessMilk() and from that method they are trying to access other programs accessCoffeePowder() method. But as the methods are synchronized they take lock on the method they first has access and once they has access they will not release it unless the process is complete. So when they try to access method from each other due to mutex condition they can not and hence form the deadlock condition.

Note: Although, Deadlocks are really rare yet you shall take every possible precaution to prevent deadlocks.

Inter Thread Communication

The efficient working of a multithreaded program depends upon the synchronization between multiple thread and for this only the use of synchronization is not enough. For example consider following scenario. You go to McD to enjoy a burger. You place an order and they prepare the burger for you. Now we can divide the process into two thread and one resource. "McD" and "Jhon" are two threads and the "Burger" is a resource.

Now, pay attention that both threads are working on the same resource and the state of the resource is important. There could be problem with the case if there is lack of communication between the different threads so it is important that you use the inter thread communication. wait(), notify() and notifyAll() are three major methods which act as utility methods for inter thread communication. The meaning and work can be easily understood by the name of method.

wait() - wait for infinite time until some condition or thread notify.
notify() - notify a waiting thread to continue its work.
notifyAll() - notify all waiting threads to get out of wait state.

Before we look into the real matter of inter thread communication let us show you an example of what happens when you don't consider using it. For this purpose we are going to use same Burger and McD example.


class BurgerN {

    public synchronized void makeBurger() {
        System.out.println("Please Wait! Burger will be ready soon, said " + Thread.currentThread().getName());
        try{ Thread.sleep(1000); }catch(InterruptedException ie){}
        System.out.println("Hey ! Burger is ready, said" + Thread.currentThread().getName());
    }

    public synchronized void eatBurger() {
        System.out.println("Okay, I am waiting, replied " + Thread.currentThread().getName());
        try{ Thread.sleep(1000); }catch(InterruptedException ie){}
        System.out.println("Burger eaten, It was yummy. said" + Thread.currentThread().getName());
    }
}

class PCN {

    static BurgerN burg = new BurgerN();

    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    burg.makeBurger();
                }
            }
        }, "McD").start();
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    burg.eatBurger();
                }
            }
        }, "John").start();
    }
}


Please Wait! Burger will be ready soon, said McD
Hey ! Burger is ready, saidMcD
Please Wait! Burger will be ready soon, said McD
Hey ! Burger is ready, saidMcD
Please Wait! Burger will be ready soon, said McD
Hey ! Burger is ready, saidMcD
Please Wait! Burger will be ready soon, said McD
Hey ! Burger is ready, saidMcD
Please Wait! Burger will be ready soon, said McD

As you can see the output is uncertain, the thread "You" is not getting any chance to work upon the Burger. however McD is keep on producing the Burgers where there should not be any more production until there is a new order. For the structure of the program, we have made a class Burger which acts as resource and we have created two threads, which work upon the resources. However the output is uncertain. I don't grantee that this will look same in your system too. It might be the opposite case that "Jhon" is consuming however the McD is not producing. So just try it.

Now, we have another version of same program which uses the ITC to work up the resources.


class Burger {
    boolean burgerStatus = false;
    public synchronized void makeBurger() {
        if (!burgerStatus) {
            try {
                System.out.println("Please Wait! Burger will be ready soon, said " + Thread.currentThread().getName());
                burgerStatus = true;
                wait();
                Thread.sleep(1000);
                System.out.println("Hey ! Burger is ready, said" + Thread.currentThread().getName());
            } catch (InterruptedException ie) {
                System.out.println(ie);
            }
            notify();
        }
    }

    public synchronized void eatBurger() {
        if (burgerStatus) {
            System.out.println("Okay, I am waiting, replied "+ Thread.currentThread().getName());
            notify();
            try{ wait(); } catch(InterruptedException ie){}
            System.out.println("Burger eaten, It was yummy. said" + Thread.currentThread().getName());
            burgerStatus = false;
        }
    }
}

class PC {
    static Burger burg = new Burger();
    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    burg.makeBurger();
                }
            }
        },"McD").start();
        new Thread(new Runnable() {
            public void run() {
                while (true) {
                    burg.eatBurger();
                }
            }
        },"John").start();
    }
}

Please Wait! Burger will be ready soon, said McD
Okay, I am waiting, replied John
Hey ! Burger is ready, saidMcD
Burger eaten, It was yummy. saidJohn
Please Wait! Burger will be ready soon, said McD
Okay, I am waiting, replied John
Hey ! Burger is ready, saidMcD
Burger eaten, It was yummy. saidJohn

Do you see the effect? yes, this is what we obtain from ITC. In this program we are using a variable burgerState which is used to check the state of the program, again two threads McD and Jhon are acting on the Burger. When McD takes access it start making the burger and goes into the wait state, and when Jhon notifies it, it continues the work and meanwhile jhon goes into the wait state. In this way, both threads work in the sync to get the proper result.

Note: notifyAll is similar to the notify however it acts on more than one thread.