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?
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.
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.
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.
Related
This question already has answers here:
What is the Re-entrant lock and concept in general?
(4 answers)
Closed 4 years ago.
So , a reentrant lock increments the count by one if the current thread acquires the lock again. what i am unable to understand is why and how that helps or benefits us?
The reason, that a reentrant lock is doing this, is to not lock the same Thread again, that has already acquired this lock.
For example: Let's say you have Thread A that is acquiring your Reentrant Lock A. Than Thread B tries to acquire the Lock A, which will result in Thread B getting blocked (more about the Thread states can be found here). Now, Thread A is trying to (again) acquire the Lock A.
Because the Reentrant lock is now highering its count, Thread A is not getting blocked. Thread A still has access over the Lock and can continue (the locks stores the informations about the depth). If he (sooner or later) releases the Lock, the count will be lowered again, to check if Thread A still need the Lock or not. If the count gets to 0, meaning Thread A has released for every time he has acquired, Thread B will get the access over the Lock.
Without reentrancy, you now would have a deadlock. Why? Because Thread A has the lock and would wait to get it again.
Concurrency can be realy complicated, reentrancy helps (just a bit) reducing this complexity.
This helps for the unusual situation when you want to call another method that also requires a lock.
ReentrantLock lock = new ReentrantLock();
public void doSomething() {
lock.lock();
try {
// Something.
} finally {
lock.unlock();
}
}
public void somethingElse () {
lock.lock();
try {
// Something else.
// We can now call another locking method without risking my lock being released.
doSomething();
} finally {
lock.unlock();
}
}
Here public can call doSomething and it will acquire the lock, do it's thing and then release the lock when unlock is called.
However, when somethingElse is called and it calls doSomething it just increases the lock count. When doSomething unlocks it does not release the lock, it merely counts the lock-count down, leaving the final unlock in somethingElse to release the lock.
The point of incrementing a count for the lock is to keep track of how many times the thread acquired the lock, so that the lock won't actually be released until the thread indicates readiness to release the lock the same number of times.
The assumption is that commands for locking will be matched with commands for releasing the lock.
Say my code enters Code Section A, which requires the lock.
Then without exiting Code Section A, it enters Code Section B which also requires the same lock. As you note we have the lock, so we needn't block.
But we'll leave Section B, and Section B is written to release the lock when we exit it. (Otherwise code that reaches Section B without already having the lock would never release the lock.)
We're still in Section A, though, so we don't want to really give up the lock yet, or another thread could take it while we're in Section A.
So when we entered Section B we incremented the lock count, which means when we exit Section B we can reduce the count by one and, seeing that it's not back to 0, not release it.
Then when Section A again releases the lock, the count falls back to 0 and that is when we really let the lock go.
This question already has answers here:
Is it safe to call a synchronized method from another synchronized method?
(3 answers)
Closed 9 years ago.
Why doesn't the code below lead to a deadlock? I mean after i call getNumber(.) the object of the class Test should be locked, so I shouldn't be able to access getNumber2(.).
class Test() {
synchronized int getNumber(int i){
return getNumber2(i);
}
synchronized int getNumber2(int i) {
return i;
}
public static void main(String[] args) {
System.out.println((new Test()).getNumber(100));
}
}
Output:
100
This is because the lock is re-entrant, meaning that it can be acquired multiple times by the same thread.
From the Java tutorial:
Reentrant Synchronization
Recall that a thread cannot acquire a lock owned by another thread. But a thread can acquire a lock that it already owns. Allowing a thread to acquire the same lock more than once enables reentrant synchronization. This describes a situation where synchronized code, directly or indirectly, invokes a method that also contains synchronized code, and both sets of code use the same lock. Without reentrant synchronization, synchronized code would have to take many additional precautions to avoid having a thread cause itself to block.
The relevant part of the JLS is ยง17.1. Synchronization:
The Java programming language provides multiple mechanisms for communicating between threads. The most basic of these methods is synchronization, which is implemented using monitors. Each object in Java is associated with a monitor, which a thread can lock or unlock. Only one thread at a time may hold a lock on a monitor. Any other threads attempting to lock that monitor are blocked until they can obtain a lock on that monitor. A thread t may lock a particular monitor multiple times; each unlock reverses the effect of one lock operation.
It doesn't lead to a deadlock because when a thread enter a synchronized method, what it does is checking that it has a lock on this, then if it doesn't, it waits until it can have the lock and get it.
When the thread enters the second synchonized method in your case, it already has the lock on the this object, so it can enter the method without blocking.
I have this class:
public class MyClass {
public MyClass(){}
public void actionA(){
synchronized(MyClass.class){
System.out.print("A");
}
}
public void actionB(){
synchronized(MyClass.class){
actionA();
}
}
}
Which one (if any) is true?
Calling actionB() will lead to a deadlock, since actionA() can never aquire the lock associated with MyClass.class
Calling actionB() will not lead to a deadlock, since it already has aquired the lock associated with MyClass.class
#2 will happen, since the calling thread has the lock.
If however the code looked like this:
public void actionB(){
synchronized(MyClass.class) {
Thread thread = new Thread(new Runnable { run() { actionA(); }});
thread.start();
thread.join();
}
}
Then you would have a deadlock. locks are acquired on a per thread basis.
I find a useful mental picture is of a shared key for a padlock. Only one thread can have the key at a time, but obviously the same key will open any lock which it fits (the key fits any lock that uses the same sync object).
As an aside, it is bad practice to synchronize on any publicly visible field, since another piece of code far removed could feasibly lock on the same object leading to unnecessary contention and possibly deadlocks.
#2 will be happen.
Reentrant Synchronization
Recall that a thread cannot acquire a lock owned by another thread.
But a thread can acquire a lock that it already owns. Allowing a
thread to acquire the same lock more than once enables reentrant
synchronization. This describes a situation where synchronized code,
directly or indirectly, invokes a method that also contains
synchronized code, and both sets of code use the same lock. Without
reentrant synchronization, synchronized code would have to take many
additional precautions to avoid having a thread cause itself to block.
I'm very confusing about these two descriptions:
"The wait method blocks the calling thread and gives up the monitor lock"
"The notify method unblocks one waiting thread but does not give up the monitor lock"
Here is my questions:
I know each object in Java has a lock, but what is the "monitor lock" means? is it the same as the oject's lock?
Why notify method needs to give up the monitor lock?
If I try to make a object waiting with the following code:
class simpleTask extends Thread
{
int waitingTime;
public simpleTask(int waitingTime)
{
this.waitingTime = waitingTime;
}
public void run()
{
synchronized(this) // this is a reference of current object
{
try {
this.wait(waitingTime);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Like the first description above, is that means the the current object is blocked by synchronized keyword, and then wait method releases the lock?
I know each object in Java has a lock, but what is the "monitor lock" means? is it the same as the object's lock?
Yes, they are the same thing. They are also occasionally called the object's "mutex" and the object's "primitive lock". (But when someone talks about Lock, they are talking about this Java interface ... which is a different locking mechanism.)
Why notify method needs to give up the monitor lock?
The notify method doesn't give up the lock. It is your code's responsibility to give up the lock (i.e. leave the synchronized block or return from the synchronized method) after the notify call returns.
Why is that necessary? Because any other thread that is currently waiting on that lock (in a wait(...) call) has to reacquire that lock before the wait call can complete.
Why did they design notify / wait like this? So that they can be used to implement condition variables.
Like the first description above, is that means the the current object is blocked by synchronized keyword, and then wait method releases the lock?
That is correct. When a thread calls someObject.wait() its lock on someObject is released ... and then reacquired (by the same thread) before the wait() call returns. Of course, in the meantime the lock someObject may have been acquired and released multiple times by other threads. The point is that when wait returns, the thread that called wait will have the lock.
Yes, the monitor lock is the same as the object's lock. If you do synchronized (object), that's the lock.
In your example, the current object will give up the lock while waiting, the wait() call gives up the lock. In another thread, notify() is called to wake the object up, and when the wait() call returns it will hold the lock again.
A monitor is a type of synchronization construct.
The reason that waiting gives up the lock is so that other threads can acquire the lock, such as other threads that might want to wait. Also: It's usual for the thread that's awakening other threads to lock before releasing any threads, to prevent a race condition.
For more about this, you should study condition variables (i.e. condvars).
I'm not sure if I'm interpreting the javadoc right. When using a ReentrantLock after calling the lock method and successfully gaining a lock, can you just access any object without any synchronized blocks and the happend-before relationship is magically enforced?
I don't see any connection between the ReentrantLock and the objects I'm working on, that's why it is hard to believe I can work on them safely. But this is the case, or am I reading the javadoc wrong?
If thread A has modified some object inside a code block CB1 guarded by the lock and then releases the lock, and thread B enters in a code block guarded by the same lock, then thread B will see the modifications done by thread A in the code block CB1.
If two threads read and write the same shared state, then every read and write to this state should be guarded by the same lock.
It's ... a (mutex) lock:
void myMethod()
{
myLock.lock(); // block until condition holds
try
{
// Do stuff that only one thread at a time should do
}
finally
{
myLock.unlock()
}
}
Only one thread can hold the lock at a time, so anything between the lock() and unlock() calls is guaranteed to only be executed by one thread at a time.
The relevant Oracle tutorial can be found here.
There's no magic in it. You're safe if, and only if, all threads accessing an object use the same lock - be it a ReentrantLock or any other mutex, such as a synchronized block.
The existence ReentrantLock is justified by that it provides more flexibility than synchronized: you can, for example, just try to acquire the lock - not possible with synchronized.