• Post Reply Bookmark Topic Watch Topic
  • New Topic

Choosing the object-to-lock in explicit locks (ReentrantLock example)

 
Toni Lane
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
So, I just finished studying synchronized keyword (Blocks and methods). And here is what i basically know.

A synchronized keyword guarantees atomicity and visibility and it has to operate on two things.
1- A thread. (The calling thread)
2- An object. (The monitor object)

When using
The object "object" will be locked on the calling thread only. until the lock is released.

So, When i started attempting to study explicit locks, I came across this example
[Source] Java 8 Concurrency Tutorial: Synchronization and Locks - Part 2: Synchronization and Locks
ReentrantLock
The class ReentrantLock is a mutual exclusion lock with the same basic behavior as the implicit monitors accessed via the synchronized keyword but with extended capabilities. As the name suggests this lock implements reentrant characteristics just as implicit monitors.
Let's see how the above sample looks like using ReentrantLock




Which brings me to my questions.
1- Is what's previously mentioned correct and generally accurate?
2- When using explicit locks (in the above ReentrantLock example), How do i get to choose the object that i want locked? Assuming that the calling thread is the thread that will have the lock.
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You decide the policy for how and which objects to lock.

In general, you create a lock for each set of operations that should not be allowed to run concurrently.

Let's say you have an object that has two completely unrelated fields:

Since the two fields are unrelated and the validity of the object doesn't depend on certain combinations of a and b, one thread should be allowed to interact with a while another interacts with b. To facilitate this, you need two different locks.

A big advantage of using explicit locks over implicit ones, is that a malicious class can't lock an object indefinitely while it goes off and does other things. For instance, if readFromA() used synchronized(this), any class that has access to an instance of MyObject can use a synchronized block to lock the instance, and then keep it locked forever. With explicit locks this doesn't happen, as long as you keep locks private.
 
Toni Lane
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
So, the rule with explicit locks is that : You can only run (from Lock#lock to Lock#unlock) one Lock-Type object at time.
Did i get it right?
 
Henry Wong
author
Sheriff
Posts: 22541
109
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Toni Lane wrote:
A synchronized keyword guarantees atomicity and visibility and it has to operate on two things.
1- A thread. (The calling thread)
2- An object. (The monitor object)


Question. What "atomicity and visibility" guarantees are you referring to? Method calls? Access to instance variables?

Simply, there are *no* method or variable guarantees, with owning a synchronization lock on an object. The only guarantee is that only one thread can own the lock (on the object) at a time. Synchronization locks are cooperative. It is the developer's responsibility to take the "only one thread can own the lock" guarantee, and make that into visibility and atomicity guarantees.

Henry
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Only one thread can lock an instance of a lock at a time.

That means that if you lock an instance of a lock, other threads can still lock other locks. The same thread can lock a lock twice, and it won't block. A thread locking a lock that has already been locked by another, will block until the other thread has unlocked the lock.
 
Toni Lane
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Henry Wong wrote:
Toni Lane wrote:
A synchronized keyword guarantees atomicity and visibility and it has to operate on two things.
1- A thread. (The calling thread)
2- An object. (The monitor object)


Question. What "atomicity and visibility" guarantees are you referring to? Method calls? Access to instance variables?

Simply, there are *no* method or variable guarantees, with owning a synchronization lock on an object. The only guarantee is that only one thread can own the lock (on the object) at a time. Synchronization locks are cooperative. It is the developer's responsibility to take the "only one thread can own the lock" guarantee, and make that into visibility and atomicity guarantees.

Henry

As a beginner, You made me even more confused.

Stephan van Hulst wrote:Only one thread can lock an instance of a lock at a time.

That means that if you lock an instance of a lock, other threads can still lock other locks. The same thread can lock a lock twice, and it won't block. A thread locking a lock that has already been locked by another, will block until the other thread has unlocked the lock.

That's exactly what i need to know, thanks.
 
Campbell Ritchie
Marshal
Posts: 52581
119
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephan van Hulst wrote:. . . The same thread can lock a lock twice, and it won't block. A thread locking a lock that has already been locked by another, will block until the other thread has unlocked the lock.
Surely that means, A thread trying to lock ... because a thread has to wait until the other thread has finished and releases the lock first.

There are limits to how often a Lock can be locked. You get problems with more than 2147483647 locks on the same object I take it OP knows to use finally blocks for all unlock() calls.
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:Surely that means, A thread trying to lock ... because a thread has to wait until the other thread has finished and releases the lock first.

Yes, you're correct.
 
Toni Lane
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:
Stephan van Hulst wrote:. . . The same thread can lock a lock twice, and it won't block. A thread locking a lock that has already been locked by another, will block until the other thread has unlocked the lock.
Surely that means, A thread trying to lock ... because a thread has to wait until the other thread has finished and releases the lock first.

There are limits to how often a Lock can be locked. You get problems with more than 2147483647 locks on the same object I take it OP knows to use finally blocks for all unlock() calls.


I didn't know before. but, when i found the example, I had to search (on why was it used) and came across results like

Java Practices -> Use finally to unlock
Brian Goetz in Java Concurrency in Practice strongly recommends the following technique for ensuring that a lock is released. It uses a try..finally style; this ensures that no matter what happens during the time the lock is held, it will eventually be unlocked. Even if an unexpected RuntimeException is thrown, the lock will still be released.


so, I am guessing that it is used to ensure a fail-safe mechanism to prevent indefinite thread locking.
 
Campbell Ritchie
Marshal
Posts: 52581
119
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Correct. If an Exception is thrown there is a risk that the lock will remain locked forever. Some people put the lock instruction in the tryIs it going to make any difference? Well consider what will happen if the lock() statement throws an exception. Doesn't program state wind back to the beginning of the line the exception occurred on? In which case it probably won't make any difference.
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
No, re-entrant locks are either locked, or they aren't. They aren't double locked, so it doesn't really matter if you put the lock() statement inside or outside the try.

This is not true for Semaphore. You *MUST* acquire a permit before you enter the try block, otherwise you run a risk of releasing too many permits. For that reason I also think it's good to get into the habit of acquiring a lock before you enter the try block.
 
Campbell Ritchie
Marshal
Posts: 52581
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You can't double‑lock a reentrant lock, but it maintains a count of how many times the owning Thread has locked it? And that count must be reduced to 0 by unlocking in order for it to be available to other Threads.
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Obviously I was very tired last night

Yes, you're correct again, making calling lock() outside the try-block the only correct way.
 
Campbell Ritchie
Marshal
Posts: 52581
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Despite what they show in the Java™ Tutorials?
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
In the page you linked to a tryLock() is performed with a guard in the finally-block. There is no risk of releasing a lock prematurely.

Even so, I'm pretty sure the tutorial is not completely free from mistakes.
 
Campbell Ritchie
Marshal
Posts: 52581
119
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
We all know there are mistakes in the Java™ Tutorials. Of course that example with bower and bowee is so complicated that nobody can find the mistakes
Is bower and bowee some sort of Dining Philosophers exercise?
 
Toni Lane
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

As far as my small test goes, unlocking what's not owned results in IllegalMonitorStateException.
Is that the reason why we shouldn't include the lock inside the try-finally block? or there is more to it?
 
Stephan van Hulst
Bartender
Posts: 6583
84
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Lock.lock() is allowed to throw unchecked exceptions. This means that if you call it inside a try-finally statement, and the exception occurs, you won't have acquired the lock, but you will still try to unlock it, causing at best an IllegalMonitorStateException, and at worst your entire application to execute in an unsynchronized state.
 
Campbell Ritchie
Marshal
Posts: 52581
119
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Does that mean that throwing an unchecked Exception from a Lock object before the try starts causes the finally not to run? Let's try it; I think that will prove Stephan correct about always having the lock before the try. If you write some crappy code with things like this in:-
while (true) lock.lock();
[and if that isn't bad code I don't know what is],
you get an unchecked exception.Now that lock will be completely unlocked one method too soon. That means another Thread will see the original calling method as guarded by an unlocked Lock, permitting it unrestricted access without synchronisation. Also, the present Thread will suffer an unchecked Exception and will be shut down, unless there is some very strange catching of exceptions going on.

∴ Stephan is right. Put the lock() call before the try.
 
Toni Lane
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks for the replies and useful information.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!