0

I have two pieces of code . First one uses synchronized blocks and it causes a deadlock which is completely understandable.

In the second code i tried to recreate the same issue , but using Reentrant locks this time. But the second code does not result in a deadlock in SOME CASES. Some cases there is a deadlock ,with nothing being printed in the console.

Can you pls explain why? Am i not using the Reentrant locks correctly?

Code causing Deadlock

package com.practice.multithreading;

public class DeadlockYoutube {

    public static void main(String[] args) {

        final String resource1="Printer";
        final String resource2="Scanner";

        Runnable run1=()->{
            synchronized (resource1) {
                System.out.println(Thread.currentThread().getName()+" : locked-> "+resource1);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized(resource2) {
                    System.err.println(Thread.currentThread().getName()+" : locked-> "+resource2);
                }
            }
        };

        Runnable run2=new Runnable(){
            @Override
            public void run() {
                synchronized (resource2) {
                    System.out.println(Thread.currentThread().getName()+" : locked-> "+resource2);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized(resource1) {
                        System.err.println(Thread.currentThread().getName()+" : locked-> "+resource1);
                    }
                }   
            }

        };

        Thread thread1= new Thread(run1);
        thread1.setName("Desktop");
        Thread thread2=new Thread(run2);
        thread2.setName("Laptop");

        thread1.start();
        thread2.start();


    }

}

Same code with Reentrant Locks/not causing deadlocks

package com.practice.multithreading;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;


//not working as expected
public class DeadlockYoutubeReentrantLocks {

    public static void main(String[] args) {

        final String resource1 = "Printer";
        final String resource2 = "Scanner";

        Lock lock1 = new ReentrantLock();
        Lock lock2 = new ReentrantLock();

        Runnable run1 = () -> {
            lock1.lock();
            lock2.lock();
            System.out.println(Thread.currentThread().getName() + " : locked-> " + resource1);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.err.println(Thread.currentThread().getName() + " : locked-> " + resource2);

            lock1.unlock();
            lock2.unlock();

        };

        Runnable run2 = new Runnable() {
            @Override
            public void run() {
                lock2.lock();
                lock1.lock();
                System.out.println(Thread.currentThread().getName() + " : locked-> " + resource2);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.err.println(Thread.currentThread().getName() + " : locked-> " + resource1);
                lock2.unlock();
                lock1.unlock();
            }

        };

        Thread thread1 = new Thread(run1);
        thread1.setName("Desktop");
        Thread thread2 = new Thread(run2);
        thread2.setName("Laptop");

        thread1.start();
        thread2.start();

    }

}

I tried reversing the order of the locks ..but the code executes fine sometimes and sometimes there is a deadlock and nothing is in the console.

Please explain the behavior.

Arpan Banerjee
  • 826
  • 12
  • 25
  • 2
    In both cases the deadlock can happen or not. You are out of control how the threads are scheduled. If the deadlock does not happen it does not mean that it cannot happen. You should write the code in a way that ensures it will never happen. – Michał Krzywański May 10 '20 at 08:18
  • But i tried running the first code multiple times in a row and it always resulted in a deadlock, while the second one does not. – Arpan Banerjee May 10 '20 at 08:20

1 Answers1

2

Your two code snippets are not equivalent.

In the first one, you

  1. Acquire lock for resource 1
  2. Sleep
  3. Acquire the lock for resource 2

In the second code (using locks)

  1. Acquire lock for resource 1
  2. Acquire the lock for resource 2
  3. Sleep

With the second, you are reducing the probability of the two threads to acquire the lock for each resource (thread 1 to acquire lock for resource 1 and thread 2 for resource 2). Thus, this reduces the percentage of times it can deadlock.


To make the 2nd snippet equivalent to the 1st,

Runnable run1 = () -> {
    lock1.lock();
    System.out.println(Thread.currentThread().getName() + " : locked-> " + resource1);
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    lock2.lock(); //<-- Moved here
    System.err.println(Thread.currentThread().getName() + " : locked-> " + resource2);

    lock1.unlock();
    lock2.unlock();

};

Runnable run2 = () -> {
    lock2.lock();
    System.out.println(Thread.currentThread().getName() + " : locked-> " + resource2);
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    lock1.lock(); //<-- Moved here
    System.err.println(Thread.currentThread().getName() + " : locked-> " + resource1);
    lock2.unlock();
    lock1.unlock();
};

This should deadlock as often your code that uses synchronized. As commented by @michalk, both the code are prone to deadlock.

Thiyagu
  • 17,362
  • 5
  • 42
  • 79
  • 1
    Good catch, I would also mention that if deadlock does not happen, it does not mean it is not there. – Michał Krzywański May 10 '20 at 08:25
  • 2
    @michalk I've linked to your comment which I agree with. I'm sure OP didn't mean that the 2nd code is better than the 1st. It was an educational post to understand why 2nd code didn't deadlock as often as the 1st. – Thiyagu May 10 '20 at 08:29