akash777.sharma akash777.sharma - 3 months ago 25
Java Question

How lock() method works in ReentrantLock java

I am trying to understand how ReentrantLock works in java.

Lets consider a simple example below :

private ReentrantLock lock;
public void foo() {
lock.lock();
try{
...
}finally {
lock.unlock();
}
}


I was trying to figure out the call hierarchy of lock() method.

public void lock() {
sync.lock();
}


For FairSync :

final void lock() {
acquire(1);
}


For NonFairSync :

final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}


Both lock() methods call acquire() method with argument as 1.

In AbstractQueuedSynchronizer class :

public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}

static void selfInterrupt() {
Thread.currentThread().interrupt();
}


If current thread cannot acquire a resource (i.e. some another thread has acquired this resource) , then current thread has to wait. In this case ReentrantLock calls selfInterrupt() method.

Now my question is how interrupt() method can stop a thread which is equivalent to wait() method in synchronized ?

Also , after the resource has been released by another thread, how currentThread start automatically? ( After calling unlock() method by another thread which is internally calling
sync.release(1);
)

I also tried to figure out how interrupt() method works from here but unable to find answer to my questions.

Answer

If current thread cannot acquire a resource (i.e. some another thread has acquired this resource) , then current thread has to wait. In this case ReentrantLock calls selfInterrupt() method.

No, if tryAcquire returns false, acquireQueued will be called. Internally, that method uses LockSupport#park to unschedule the thread. The javadoc states

If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes disabled for thread scheduling purposes and lies dormant until one of three things happens:
- [..]
- Some other thread interrupts the current thread;

It doesn't actually throw an InterruptedException in this case, the method just returns. To check if the corresponding thread was woken up due to an interrupt, it has to use Thread#interrupted() which returns true or false depending, but also clears the interrupt flag on the corresponding Thread instance.

So the acquireQueued propagates that interrupted value back up and lets acquire reset the interrupt flag on the Thread if needed in that little piece of code

if (!tryAcquire(arg) &&
    acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    selfInterrupt();

Also , after the resource has been released by another thread, how currentThread start automatically?

Again, it makes uses of the LockSupport utility with unpark

Makes available the permit for the given thread, if it was not already available. If the thread was blocked on park then it will unblock.

Comments