Understanding lock scope
Asked Answered
L

3

8

From this link, I understand "Since the lock() and unlock() method calls are explicit, we can move them anywhere, establishing any lock scope, from a single line of code to a scope that spans multiple methods"

So what I understand from the above statement is

public class Test {
    Lock l = new ReentrantLock();

    void myMethod1() {
        l.lock();
        // Do my stuff here
    }

    void myMethod2() {
        // Do more stuff here
        l.unlock();
    }
}

So basically 1 can call method1 and method2 in sequence and assume the call is thread safe.

I am not sure if it's true as said above.

What if somebody just calls method2 when i am already executing method1/method2 pair? Doesn't it complicate things.

I think a lock should be acquired and released in the function itself, before the control is returned from the function. Is my understanding correct?

Lahdidah answered 14/7, 2015 at 11:27 Comment(2)
I think a lock should be acquired and released in the function itself, before the control is returned from the function. It is a lot easier to do it this way but it isn't necessary with reentrantlock.Animalist
If you use error-prone with LockMethod and UnlockMethod annotations then you'll get a compile time check.Lifelong
A
5

Answer to first question:

What if somebody just calls method2 when i am already executing method1/method2 pair? Doesn't it complicate things.

Suppose another thread calls the unlock() method on the ReentrantLock object then IllegalMonitorStateException would be thrown. Because the thread is not acquiring the lock and when it tries to unlock then it get exception.
It will not have any effect on execution or locking of first thread which is acquiring the lock.

  • Same thread:

    • Lock: If same thread which is acquiring the lock again tries to acquire the lock then lock counter increments.
    • Unlock: If same thread which is acquiring the lock tries to unlock then lock counter decrements and as soon as lock counter becomes 0, thread releases the lock.
  • Different thread:

    • Lock: If the lock is held by another thread then the current thread becomes disabled for thread scheduling purposes and lies dormant until the lock has been acquired, at which time the lock hold count is set to one.
    • Unlock: If different thread tries to unlock when it is NOT holding the lock then IllegalMonitorStateException is thrown.

That is the reason ReentrantLock lock and unlock mandates you to have try-catch or throw mechanism because it throws exception.

Read below excerpt from ReentrantLock#unlock()

If the current thread is the holder of this lock then the hold count is decremented. If the hold count is now zero then the lock is released. If the current thread is not the holder of this lock then {@link IllegalMonitorStateException} is thrown.

Answer to second question:

I think a lock should be acquired and released in the function itself, before the control is returned from the function. Is my understanding correct?

That's the whole purpose of ReentrantLock, you can extend the locking mechanism to other methods, which you cannot do with synchronized blocks and methods. See below from ReentrantLock

A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized methods and statements, but with extended capabilities.

Aggressor answered 14/7, 2015 at 12:18 Comment(2)
Don't use code formatting for text that isn't code.Fetterlock
@EJP Updated for remove code format and put in quotes.Aggressor
S
3

What if somebody just calls method2 when i am already executing method1/method2 pair? Doesn't it complicate things.

java.util.concurrent.locks.Lock is a more powerful mechanism than synchronized. Power tools are dangerous. Use them with caution.

See @hagrawal's answer for details.

Selfpreservation answered 14/7, 2015 at 12:43 Comment(1)
"Power tools are dangerous. Use them with caution." True.Aggressor
A
1

Consider this example instead:

public class Test {
    ...
    public void method1() {
       l.lock();
       ...
    }

    public void method2() {
       l.lock();
       ...
       while (l.isHeldByCurrentThread()) {
         l.unlock();
       }
    }
}

This setup means that once Thread A calls method1(), Thread B will block when calling any of the methods until Thread A calls method2(). So the lock scope spans zero or more invocations of method1() followed by a single invocation of method2().

Though in practice I would consider it far easier and cleaner to write code that restricts lock scope to a single method or part of a method.

Animalist answered 14/7, 2015 at 11:38 Comment(3)
This won't work! The reentrant lock needs one unlock() per lock(). Down vote.Smithereens
@Smithereens is right. lock() increments the lock count, and unlock() decrements the lock count. The lock will only be released if the lock count goes to zero. docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/…Monied
And this neatly demonstrates why it isn't a good idea to try wacky scopes for locks: it's very easy to get it wrong.Animalist

© 2022 - 2024 — McMap. All rights reserved.