Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ReentrantLock.lock() doesn't block other threads

I'm having a hard time understanding the behavior of ReentrantLock.lock()

I have the following class

import java.util.concurrent.locks.*;

class BlockingClass {

    private Lock lock = new ReentrantLock();

    private Condition condition = lock.newCondition();

    public void a() {
        lock.lock();
        System.out.println("called in method a(): lock()");

        try {
            System.out.println("called in method a(): await()");
            condition.await();
        } 
        catch (InterruptedException e) {} 
        finally {
            lock.unlock();
            System.out.println("called in method a(): unlock() ");
        }
    }

    public void b() {
        lock.lock();
        System.out.println("called in method b(): lock()");

        System.out.println("called in method b(): signalAll()");
        condition.signalAll();

        lock.unlock();
        System.out.println("called in method b(): unlock() ");
    }
}

which I run with the following test:

class BlockingClassTest {
    public static void main(String[] args) throws InterruptedException {

        final BlockingClass blockingClass = new BlockingClass();

        new Thread() {
            public void run() {
                System.out.println("Thread1 calling a()");
                blockingClass.a();
            }
        }.start();

        Thread.sleep(1000); 

        new Thread() {
            public void run() {
                System.out.println("Thread2 calling b()");
                blockingClass.b();
            }
        }.start();
    }
}

I would have expected a deadlock. Once the a() method calls lock.lock(), I would expect that anyone calling the b() method would have to wait at b's lock.lock() until the thread calling a() had called lock.unlock(). But since a() is waiting for b() to call condition.signalAll(), both methods should have stayed blocked forever.

Instead, this is the output I get in the console:

Thread1 calling a()
called in method a(): lock()
called in method a(): await()
Thread2 calling b()
called in method b(): lock()
called in method b(): signalAll()
called in method a(): unlock() 
called in method b(): unlock() 

What am I misunderstanding about the proper use and functioning of lock() and unlock()?

like image 884
Student Avatar asked Jan 11 '23 02:01

Student


1 Answers

You're not misunderstanding ReentrantLock, you're misunderstanding Condition. A Condition is bound to a lock and Condition.await() will effectively unlock, check and wait, and then relock the lock. See Condition.await().

In a(), between lock() and the call to await(), and between the return of await() and unlock(), your lock is behaving as you expect. Inside the call to await(), the Condition is managing it.

This is part of the general concept of a "condition variable"; it's why any thread library you find associates a lock of some sort with a condition (e.g. in POSIX C, pthread_cond_wait requires both a condition variable and a mutex).

Check out the Wikipedia article on condition variables, it explains this behavior and the reasons for it in detail.

like image 101
Jason C Avatar answered Jan 17 '23 17:01

Jason C