为什么我们需要在同一个对象上同步以使notify()工作

时间:2013-08-07 05:26:14

标签: java synchronization wait notify

我得到了java.lang.IllegalMonitorStateException。我提到this问题,它解决了我的问题。第一个答案是

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();
}  

我的问题是为什么我们需要在同一个对象广告上同步它?

据我所知,当我们说

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

我们在对象someObject上获得一个锁,然后我们在它上面调用wait()。 现在,另一个线程怎么能锁定同一个对象来调用notify()?我缺少什么?

4 个答案:

答案 0 :(得分:8)

为什么notify也需要锁定?

想象一下这种情况:

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

现在想象一下notify在其他地方没有任何锁定:

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

乍一看,整个声音始终按预期工作 但是,想象一下这种竞争条件:

//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!
     }
}

如果我们有办法告诉notify方:

线程1:“哼哼.. notify,你很可爱,但我刚刚开始评估我的情况(x.count < 4)为真,所以请...不要愚蠢刚刚发送您的预期通知(在我将状态等待之前),否则,等待已经通过的事情我会很荒谬“

线程2:“好的好吧......为了保持一致,我会锁定我的逻辑,以便在等待调用后发送我的通知释放我们的共享锁,从而你将收到此通知,允许退出等待状态;)“

因此,总是在notify侧,在等待的同一对象上放置一个锁,以避免这种情况并让关系始终保持一致。

<强> =&GT;导致notify的逻辑和导致wait的逻辑不应重叠。

答案 1 :(得分:5)

根据Object#wait()

的javadoc
  

当前主题必须拥有此对象的监视器。线程发布   此监视器的所有权,并等待另一个线程通知   在这个对象的监视器上等待通过a唤醒的线程   调用notify方法或notifyAll方法。线程然后   等到它可以重新获得监视器的所有权并恢复   执行。

要使用wait / notify,线程必须具有锁定,否则 IllegalMonitorStateException被抛出

  

抛出: IllegalMonitorStateException - 如果当前线程不是   对象监视器的所有者。

为什么

所以,  wait()使当前线程释放锁定

notify()发出其他等待线程的信号,然后尝试获取锁。

要么这样做,当前线程必须具有锁定。这很有道理!

修改

  • 为什么线程调用wait()保持锁定现在非常明显。

  • 但是为什么线程调用notify()会持有锁?好吧,首先,要证明它的authenticity。否则,任何线程都可能继续触发false notifications,并且waiting线程将继续被中断。 幸好情况并非如此。

答案 2 :(得分:1)

wait / notify通常用于等待某个其他线程完成任务,或者等到满足某个条件。

假设我们有一个名为 objectA 的对象和两个名为 thread1和thread2 的线程。
thread1 具有一些线程安全任务,因此它使用 synchronized 块获取objectA的监视器。

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

在java调用中,wait()意味着释放监视器,以便其他线程可以获取此监视器并完成其任务,当前线程进入某个状态,称为等待状态,用于objectA的监视器。

     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.
     }

现在,thread2可以拥有objectA的监视器并执行其任务。

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

任务完成后,它会通知处于等待状态的其他线程,它会在其拥有的对象上释放监视器。 请注意,要调用 notify(),线程也应该是对象监视器的所有者

       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
     }

在objectA上调用wait()并在其他对象上调用notify()(比如说objectB)对 thread1 没什么用。来自 thread1 等待获取监视器on objectA 而不是其他对象(比如说objectB)。

更新Why obtain monitor to call notify()
要调用 notify()我们需要获取监视器,因为可以保证两个线程试图在一个对象上调用notify()不会踩到彼此的脚趾(以避免竞争条件)。登记/> why we need to get lock before notify

答案 3 :(得分:0)

查看Hotspot JVM源代码,我发现:notify()方法修改了对象监视器的等待集。 (对象的等待集是在其上调用wait()的线程集。)如果对等待集的访问未同步,则可能发生坏事:例如,可以从集合中删除线程永远被唤醒在调用notify()之前要求调用线程拥有监视器解决了这个问题,尽管其他解决方案也可能存在。

还有其他一些参数,例如在没有持有监视器的情况下调用通知通常意味着程序员错误,但我认为这不足以激发这样的限制。