6

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?

4

4 回答 4

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)为真,所以请......不要傻傻地发送你预期的通知(在我把我的状态之前等待),否则,我等待已经过去的事情会很可笑”

Thread2:“好的好的...我将锁定我的逻辑以保持一致,以便在您的等待调用释放我们的共享锁发送我的通知,因此您将收到此通知,允许退出您的等待地位 ;)”

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

=> 导致 anotify的逻辑和导致 a 的逻辑wait不应重叠。

于 2013-08-07T08:52:57.903 回答
5

根据Object#wait()的 javadoc

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

要使用等待/通知线程必须有锁,否则 抛出IllegalMonitorStateException

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

为什么

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

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

做任何一个,当前线程必须有锁。这是有道理的!

编辑

  • 为什么线程调用 wait() 必须持有锁现在很明显了。

  • 但是为什么线程调用 notify() 必须持有锁呢?好吧,一方面,证明它authenticity。否则,任何线程都可以继续触发false notifications,并且waiting线程将继续被中断。值得庆幸的是,情况并非如此。

于 2013-08-07T05:28:16.543 回答
1

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

假设我们有一个名为objectA的对象和两个名为thread1 和 thread2的线程。
thread1有一些线程安全的任务,所以它使用同步块获取 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等待在 objectA 上不是在其他对象上(比如说 objectB)。

更新 Why obtain monitor to call notify()
为调用notify()我们需要获取监视器,因为可以保证尝试在一个对象上调用 notify() 的两个线程不会踩到对方的脚趾(避免竞争条件)。
为什么我们需要在通知之前获得锁

于 2013-08-07T06:08:51.430 回答
0

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

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

于 2013-08-07T09:57:24.797 回答