Java:Does Wait() Release Lock from Synchronized Block

Lock released after calling wait() method?

Yes, T1 will release the lock when wait, and it has to re-aquire the lock after getting notified. See the details in java language specification.

And, the wait method should be called in a while loop.

 synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}

How does multiple threads acquire and release lock in synchronized methods and synchronized blocks in java?

In case of biased locking: if the lock is biased towards a certain thread, no CAS is needed; just a volatile write. Biased lock information is kept in the mark word of the object header. Biased locking is going to be removed from JDK 15.

If a lock is contended, the object-monitor is used for synchronization. By default the object monitor is deflated, but if there is contention or you do a wait/notify, then the monitor gets inflated and is attached to the object.

On Linux blocking behavior is implemented using a wait-queue. So when a thread needs to wait for a lock, it is removed from the scheduler and added to the wait queue. When a lock unlocks, the thread on the wait queue is reinserted back into the scheduler.

The reason why the code doesn't throw an exception is that the monitor is read only once when the synchronized block is entered.

PS: It could be that your lock get completely removed due to lock elision. If the JIT can provide no other thread can acquire that lock, there is no point in synchronizing.

Why must wait() always be in synchronized block

A wait() only makes sense when there is also a notify(), so it's always about communication between threads, and that needs synchronization to work correctly. One could argue that this should be implicit, but that would not really help, for the following reason:

Semantically, you never just wait(). You need some condition to be satsified, and if it is not, you wait until it is. So what you really do is

if(!condition){
wait();
}

But the condition is being set by a separate thread, so in order to have this work correctly you need synchronization.

A couple more things wrong with it, where just because your thread quit waiting doesn't mean the condition you are looking for is true:

  • You can get spurious wakeups (meaning that a thread can wake up from waiting without ever having received a notification), or

  • The condition can get set, but a third thread makes the condition false again by the time the waiting thread wakes up (and reacquires the monitor).

To deal with these cases what you really need is always some variation of this:

synchronized(lock){
while(!condition){
lock.wait();
}
}

Better yet, don't mess with the synchronization primitives at all and work with the abstractions offered in the java.util.concurrent packages.

Using `synchronized` code blocks with `.wait` and `.notify` in Java

You are seeing an example of thread starvation.

One way for starvation to happen is if you write loops like this:

while (true) {
synchronized(lock) {
...
}
}

The problem is, the very next thing that the thread does after releasing lock is, it locks it again. If any other thread currently is blocked awaiting the same lock, then the thread that executes this loop is almost certain to win the race to lock it again because the thread that's executing the loop is already running, but the other thread needs time to "wake up."

We say that the other thread is "starved" in that case.

Some threading libraries offer a so-called fair lock, which avoids starvation by ensuring that the lock always will be awarded to whichever thread has been waiting the longest. But fair locks usually are not the default because they hurt the performance of better-designed programs in which locks are not so heavily contested.


In your example, the starvation is not a total disaster because each thread calls wait() when it runs out of work to do. That releases the lock and allows the other thread to run. But it pretty much forces the threads to "take turns:" One will always be sleeping while the other is working. You might just as well have written it as a single-threaded program.


It's better if your threads don't keep any lock locked longer than absolutely necessary:

while (true) {
int val;
synchronized(queue_lock) {
if (queue.isEmpty()) {
lock.wait();
}

val = queue.remove();
queue_lock.notify();
}
System.out.println(val + ": " + queue.size());
}

Here I've moved the println(...) call out of the synchronized block. (I've also renamed your lock variable to emphasize that it's purpose specifically is to protect the queue.)

You could do the same in the producer thread by moving the random() call out of the synchronized block. In that way you get more opportunity for the two threads to run in parallel--the producer can be working to produce each new thing while the consumer is simultaneously dealing with some thing that it has "consumed."


Just to clarify: Here's what might actually happen:

producer                              consumer
--------------------------------- -----------------------------------
enter synchronized block
tries to enter synchronized block queue.isEmpty() => true
lock.wait()
...releases the lock...
enters synchronized block ...awaiting notification...
queue.add(...) ...awaiting notification...
lock.notify() ...now awaiting the lock...
leave synchronized block ...starts to wake up, but...
enter synchronized block ...Dang! Lost the race...
queue.add(...) ...awaiting the lock...
lock.notify()
leave synchronized block ...starts to wake up, but...
enter synchronized block ...Dang! Lost the race...
. ...awaiting the lock...
. .
. .
queueSize() > 10 .
lock.wait()
...releases the lock... ...starts to wake up, and...
...awaiting notification... ...FINALLY! re-acquire the lock, and...
. lock.wait() returns
. val = queue.remove()
. ...
...now awaiting the lock... lock.notify()
...starts to wake up, but... leave synchronized block
...Dang! Lost the race... enter synchronized block
. .
. .
. .

How does wait() get the Lock back in Java

When Thread1 is notified the thread has to acquire the lock before it can exit the wait method, see the java doc for Object#wait:

The thread T is then removed from the wait set for this
object and re-enabled for thread scheduling. It then competes in the
usual manner with other threads for the right to synchronize on the
object; once it has gained control of the object, all its
synchronization claims on the object are restored to the status quo
ante - that is, to the situation as of the time that the wait
method was invoked. Thread T then returns from the
invocation of the wait method. Thus, on return from the
wait method, the synchronization state of the object and of thread
T is exactly as it was when the wait method was
invoked.

Does synchronized (this) lock only the synchronized block or all the this code?

Using synchronized means in order for a thread to execute that block or method, it has to acquire a lock referenced (explicitly or implicitly) by that block or method. For the static synchronized methods, that lock is the monitor on the class object. For the synchronized(this) block, the lock used is the monitor on the current instance. Sharing of locks between multiple methods or blocks is what enforces atomicity and memory visibility of updates, also the shared lock provides a shared communication path through which waiting and notification can take place.

Since the static synchronized blocks use a different lock from that used by the block in the constructor, entering a static synchronized block is not blocked by another thread's accessing the block that requires acquiring the lock on the current instance, and the synchronized block in the constructor has no effect on anything, the lock acquisition will always be uncontended. More importantly here, changes made by one thread in the constructor may not get seen by other threads using the getter. Synchronization affects both locking and memory visibility.

This changed version would work:

public class ObjectCounter {
private static long numOfInstances = 0;
public ObjectCounter(){
synchronized(ObjectCounter.class){
numOfInstances++;
}
}
public static synchronized long getCount(){
return numOfInstances;
}
}

because the getter and the incrementing block are using the same lock. Making the different threads acquire the same monitor ensures that the change to the counter gets safely published so that another thread accessing the getter can see the updated value.

The synchronized keyword says, "you have to acquire a lock before you can enter", where for the method the lock is assumed: with the static keyword on the method it's the monitor on the class, without a static keyword it's the monitor on the current instance. For locking to work correctly the different blocks and methods need to use the same lock. There is arguably too much syntax sugar and too much making things convenient in how Java was designed: allowing implicit choice of locks and putting the monitor on java.lang.Object can cause confusion.

WRT your question #6: For what you're doing here you'd be better off with an AtomicLong. Use synchronized blocks for coordinating multiple changes that need to take place without interference from other threads.

Questions #3, #7 and #8 seem very similar: If a method/block isn't attempting to acquire a lock, nothing prevents threads from executing that method/block. The object as a whole doesn't get any protection, using the synchronized methods or blocks to enforce locking is what does the protecting. Think less in terms of "using the synchronized keyword" and more in terms of what lock threads need to acquire.



Related Topics



Leave a reply



Submit