Thread Sleep method behaviour in synchronized block

655 views Asked by At
public class DeadlockDemo2 {

    public static Object Lock1 = new Object();
    public static Object Lock2 = new Object();

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ThreadDemo1 demo1 = new ThreadDemo1();
        ThreadDemo2 demo2 = new ThreadDemo2();
        demo1.start();
        demo2.start();
    }

    private static class ThreadDemo1 extends Thread {
        public void run() {
            synchronized (Lock1) {
                System.out.println("Thread 1: Holding lock 1...");

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
                System.out.println("Thread 1: Waiting for lock 2...");

                synchronized (Lock2) {
                    System.out.println("Thread 1: Holding lock 1 & 2...");
                }
            }
        }
    }

    private static class ThreadDemo2 extends Thread {
        public void run() {
            synchronized (Lock2) {
                System.out.println("Thread 2: Holding lock 2...");

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                }
                System.out.println("Thread 2: Waiting for lock 1...");

                synchronized (Lock1) {
                    System.out.println("Thread 2: Holding lock 1 & 2...");
                }
            }
        }
    }
}

In above program, both Threads are sleeping for 10 milliseconds. So once the time expires, demo1 can acquire lock on lock2 and demo2 on lock1. But It does not happen so. They go under deadlock.

Can somebody explain the reason?

Thanks in advance.

1

There are 1 answers

4
aruisdante On

We'll ignore the design issues here and assume you're just trying to understand threading with a toy example.

The problem is your lock scoping. Let's list out the order of operations here:

In ThreadDemo1:

  1. Acquire Lock1
  2. Sleep
  3. Acquire Lock2
  4. Free Lock2
  5. Free Lock1

Similarly, in ThreadDemo2:

  1. Acquire Lock2
  2. Sleep
  3. Acquire Lock1
  4. Free Lock1
  5. Free Lock2

As you can see from the order of operations here, both of your ThreadDemo classes attempt to acquire the other lock before freeing their initial lock. This basically guarantees deadlock as they will be stuck forever waiting for the other to release their initial lock.

What you actually wanted to happen was this in ThreadDemo1:

  1. Acquire Lock1
  2. Sleep
  3. Free Lock1
  4. Acquire Lock2
  5. Free Lock2

And this in ThreadDemo2:

  1. Acquire Lock2
  2. Sleep
  3. Free Lock2
  4. Acquire Lock1
  5. Free Lock1

To do this, simply change this:

private static class ThreadDemo1 extends Thread {
    public void run() {
        synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }
            System.out.println("Thread 1: Waiting for lock 2...");

            synchronized (Lock2) {
                System.out.println("Thread 1: Holding lock 1 & 2...");
            }
        } // <----- We're going to move this bracket
    }
}

To this:

private static class ThreadDemo1 extends Thread {
    public void run() {
        synchronized (Lock1) {
            System.out.println("Thread 1: Holding lock 1...");

            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
            }
        } // <----- We moved it here
        System.out.println("Thread 1: Waiting for lock 2...");

        synchronized (Lock2) {
            System.out.println("Thread 1: Holding lock 1 & 2...");
        }
    }
}

And make the same change in your ThreadDemo2 class