Why do we need to synchronize on the same object for notify() to work

▼魔方 西西 提交于 2019-12-30 04:51:09

问题


I was getting java.lang.IllegalMonitorStateException. I referred this question and it solved my problem. The first answer is

To be able to call notify() you need to synchronize on the same object.

synchronized (someObject) {
    someObject.wait();
}

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}  

My question is why we need to synchronize on the same object ad how it works?

As far as my understanding goes when we say

synchronized (someObject) {
    someObject.wait();
}

we get a lock on object someObject and then we call wait() on it. Now how can another thread get lock on same object to call notify() on it? What am I missing?


回答1:


Why does notify need a lock too?

Imagine this scenario:

synchronized(x){
        while(x.count < 4) {
          x.wait();
          //...
        }
}

Imagine now a notify elsewhere without any lock around it:

//...
println(x.count); // print 3
x.count++;
if(count == 4) 
  x.notify()
//...

At first glance, the whole sounds to always work as expected.
However, imagine this race condition:

//Thread1 enters here
synchronized(x){
     while(x.count < 4) {
         //condition is judged true and thread1 is about to wait 
         //..but..ohh!! Thread2 is prioritized just now !
         //Thread2, acting on notify block side, notices that with its current count incrementation, 
         //count increases to 4 and therefore a notify is sent.... 
         //but...but x is expected to wait now !!! for nothing maybe indefinitely !
       x.wait();
       //maybe block here indefinitely waiting for a notify that already occurred!
     }
}

If only we had a way to tell this to notify side:

Thread 1: "Humm..notify, you are cute but I've just started to evaluate my condition (x.count < 4) to true, so please... don't be silly by sending your expected notification just now (before I put my status to waiting), otherwise, I would be ridiculous to wait for a thing that already passed"

Thread2: "Ok ok... I will put a lock around my logic in order to stay consistent, so that I send my notification after your wait call releases our shared lock, and thus you will receive this notif, allowing to quit your waiting status ;)"

Thus, always place a lock on the notify side, on the same object that is hold by wait, in order to avoid this situation and let the relationship always consistent.

=> A logic leading to a notify and a logic leading to a wait should never overlap.




回答2:


As per the javadoc for Object#wait()

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

To use wait/notify the thread must have the lock, otherwise IllegalMonitorStateException is thrown

Throws: IllegalMonitorStateException - if the current thread is not the owner of the object's monitor.

Why

So, wait() makes current thread release the lock

notify() signals other waiting thread(s) which then try to acquire the lock.

For doing either, the current thread must have the lock. And it makes sense!

Edit

  • Why must thread calling wait() hold the lock is pretty obvious now.

  • But why must thread calling notify() hold the lock? Well, for one, to prove its authenticity. Otherwise any thread could keep firing false notifications and the waiting threads would keep getting interrupted. Thankfully that is not the case.




回答3:


wait/notify are typically used to wait for some other thread to accomplish a task, or to wait until a certain condition is satisfied.

Lets say we have an object called objectA and two threads called thread1 and thread2.
thread1 has some thread safe task,so it acquires objectA's monitor using synchronized block.

       synchronized (objectA) {
          //here thread1  owns objectA's monitor
       }  

In java calling wait() means releasing monitors so that other threads can get this monitor and achieve its tasks and current thread goes into some state called waiting state for objectA's monitor.

     synchronized(objectA){
        //here thread1 owns objectA's monitor.
        objectA.wait();
        //here thred1 releases monitor of objectA's monitor and goes into waiting state and waits to get objectA's monitor once again to complete its task.
     }

Now thread2 can own the objectA's monitor and excutes its task.

    synchronized(objectA){
        //here thread2 owns objectA's monitor.
        //some task;
     }

Once task completes it notifies the other threads in waiting state that it is released the monitor on the object it owns. please note that to call notify() also thread should be owner of object monitor.

       synchronized(objectA){
        //here thread2 owns objectA's monitor.
        //some task;
        objectA.notify();
       //it signals some other thread that it can wake up from wait,so that other waiting threads can owns objectA's monitor
     }

here calling wait() on objectA and calling notify() on other object(lets say objectB) is makes no use to thread1.Since thread1 waiting to get monitor on objectA not on other object(lets say objectB).

update Why obtain monitor to call notify()
to call notify() we need to obtain monitor,because it is guaranteed that two threads trying to call notify( ) on one object won’t step on each other’s toes(to aviod race condition).
why we need to get lock before notify




回答4:


Looking through the Hotspot JVM source code I found this: The notify() method modifies the wait set of the object's monitor. (The wait set of an object is the set of threads that have called wait() on it.) If the access to the wait set was not synchronized bad things could happen: for example a thread could be dropped from the set without ever being awoken. Requiring that the calling thread owns the monitor before calling notify() solves this issue, though other solutions could also exist.

There are also other arguments, such as notify being called without holding the monitor often implying a programmer error, but I don't think that's enough to motivate such a restriction.



来源:https://stackoverflow.com/questions/18095306/why-do-we-need-to-synchronize-on-the-same-object-for-notify-to-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!