Search code examples
javadeadlock

Deadlock in Java with ReentrantLock and Lock


This snippet shows an example of a deadlock in Java by forcing Thread1 to acquire lock1 on resource1 and Thread2 to acquire lock2 on resource2. Then Thread1 tries to acquire lock2 and Thread2 tries to acquire lock1.This causes a deadlock.

I was expecting to see Thread2 and Thread1 state to be BLOCKED according to this, instead, they are in WAITING state indefinitely when I monitor the status with thread.getState().

Can you help me understand why?

Consider that the locks are declared in the main class as:

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

This is the code of the ThreadClass

import java.util.concurrent.locks.Lock;

public class ThreadClass extends Thread {
    private Lock lock1; 
    private Lock lock2;
    private Resource resource1; 
    private Resource resource2;

    public ThreadClass(Resource resource1, Resource resource2, String name, Lock lock1, Lock lock2) {
        this.resource1 = resource1;
        this.resource2 = resource2;
        this.lock1 = lock1;
        this.lock2 = lock2;
        this.setName(name);
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("Thread1")) {
            lock1.lock();
            System.out.println(Thread.currentThread().getName() + ": Using " + resource1.name);
            // Simulating work on RESOURCE 1
               
            lock2.lock();
            System.out.println(Thread.currentThread().getName() + ": Using " + resource2.name);
            // Simulating work on RESOURCE 2
            lock2.unlock();

            lock1.unlock();
        }
        if (Thread.currentThread().getName().equals("Thread2")) {
            lock2.lock();


            // Simulating work on RESOURCE 2

            lock1.lock();
            System.out.println(Thread.currentThread().getName() + ": Using resource1");
            lock1.unlock();

            lock2.unlock();
        }
    }
}

Solution

  • The documentation you refer to is about the intrinsic locks - the ones built into every Java object (also called monitors). They are a low level, slightly legacy mechanism with known caveats. The intrinsic locks are used with the synchronized keyword and are only taken when a thread enters a block of code statically marked as synchronized.

    You are using a different, higher level API, so whatever the documentation says about locks - does not apply. I have never looked inside the implementation of the ReentrantLock, but - as you can see from the Threads status - it's not using the synchronized block to park the thread, but rather a call to the Object's wait method (which is always made from inside a synchronized block and forms the other low-level primitive upon which Java's threading is built).