May 03, 2020 /   java   deadlock   concurrency   multithreading   threads

Android Save View as Image

Deadlock situation arises when two or more threads try to acquire lock on a resources which is already locked by one of them and none of application thread is able to proceed.

Deadlock condition in an application

Consider this simple but deadlock scenario including two threads A and B

Thread A locks Resource R2
.....

Thread B locks Resource R1
.....

Thread A tries to take lock on Resource R1 // but R1 is already locked by Thread B
.....

Thread B tries to take lock on Resource R2 // but R2 is already locked by Thread A


None of Thread A or B are able to proceed now.

Understanding Deadlock by an Example

Below is a step by step source code in Java to create a deadlock

Define a resource

class Resource {}

Create an application which requires two resources

class Application {

    private Resource a;
    private Resource b;

    public Application(Resource a, Resource b) {
        this.a = a;
        this.b = b;
    }
}

Create two procedures in Application class. Both the procedures requests lock on resources a and b in reverse order

    public void procedureA() throws InterruptedException{
        System.out.println("procedureA: taking lock on resource a");
        synchronized (a) {
            /**
             * Let this thread sleep for 1 second
             */
            Thread.sleep(1000);
            System.out.println("procedureA: taking lock on resource b");
            synchronized (b) {
                System.out.println("procedureA executed");
            }
        }
    }

    public void procedureB() throws InterruptedException {
        System.out.println("procedureB: taking lock on resource b");
        synchronized (b) {
            /**
             * Let this thread sleep for 1 second
             */
            Thread.sleep(1000);
            System.out.println("procedureB: taking lock on resource a");
            synchronized (a) {
                System.out.println("procedureB executed");
            }
        }
    }

Create two threads to run procedureA and procedureB concurrently

class ApplicationThreadA implements Runnable {

    private Application application;
    public ApplicationThreadA(Resource a, Resource b) {
        this.application = new Application(a, b);
    }

    @Override
    public void run() {
        try {
            application.procedureA();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ApplicationThreadB implements Runnable {

    private Application application;
    public ApplicationThreadB(Resource a, Resource b) {
        this.application = new Application(a, b);
    }

    @Override
    public void run() {
        try {
            application.procedureB();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

The Driver program

public class Deadlock {

    public static void main(String[] args) {

        /**
         * Create two resources
         */
        Resource a = new Resource();
        Resource b = new Resource();

        /**
         * Create two application threads
         */
        Thread threadA = new Thread(new ApplicationThreadA(a,b));
        Thread threadB = new Thread(new ApplicationThreadB(a,b));

        /**
         * Start both the thread
         */
        threadA.start();
        threadB.start();

    }
}

Output

procedureA: taking lock on resource a
procedureB: taking lock on resource b
procedureA: taking lock on resource b
procedureB: taking lock on resource a


You will see that after printing above lines, program does not quit. It is still waiting because it in deadlock situation

Complete code listing

Deadlock.java

class Resource {}

class Application {

    private Resource a;
    private Resource b;

    public Application(Resource a, Resource b) {
        this.a = a;
        this.b = b;
    }

    public void procedureA() throws InterruptedException{
        System.out.println("procedureA: taking lock on resource a");
        synchronized (a) {
            /**
             * Let this thread sleep for 1 second
             */
            Thread.sleep(1000);
            System.out.println("procedureA: taking lock on resource b");
            synchronized (b) {
                System.out.println("procedureA executed");
            }
        }
    }

    public void procedureB() throws InterruptedException {
        System.out.println("procedureB: taking lock on resource b");
        synchronized (b) {
            /**
             * Let this thread sleep for 1 second
             */
            Thread.sleep(1000);
            System.out.println("procedureB: taking lock on resource a");
            synchronized (a) {
                System.out.println("procedureB executed");
            }
        }
    }
}
class ApplicationThreadA implements Runnable {

    private Application application;
    public ApplicationThreadA(Resource a, Resource b) {
        this.application = new Application(a, b);
    }

    @Override
    public void run() {
        try {
            application.procedureA();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class ApplicationThreadB implements Runnable {

    private Application application;
    public ApplicationThreadB(Resource a, Resource b) {
        this.application = new Application(a, b);
    }

    @Override
    public void run() {
        try {
            application.procedureB();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}


public class Deadlock {

    public static void main(String[] args) {

        /**
         * Create two resources
         */
        Resource a = new Resource();
        Resource b = new Resource();

        /**
         * Create two application threads
         */
        Thread threadA = new Thread(new ApplicationThreadA(a,b));
        Thread threadB = new Thread(new ApplicationThreadB(a,b));

        /**
         * Start both the thread
         */
        threadA.start();
        threadB.start();

    }
}