1

我有下面的方法foo,它需要一个观察者并将当前线程置于睡眠状态,直到观察者唤醒它。

出于某种原因,我不断收到java.lang.IllegalMonitorStateException异常foo

public void foo(Observer o)
{
    Thread currentThread = Thread.currentThread();
    o.setThread(currentThread);
    // sleep until the observer wakes it
    currentThread.wait(2000);   // <<<<< Exception happens here
}

Observer对象会currentThread.notifyAll()在稍后调用它时Observable调用update

public class Observer
{
    private volatile Thread currentThread;
    // ... other code  ....

   public void setThread(Thread t)
   {
       currentThread = t;
   }

   public void update(Observable o)
   {
        currentThread.notify();
   }
}

知道这里有什么问题吗?

4

5 回答 5

3

每当您调用object 的wait(long)ornotify()方法时,该线程必须拥有该对象的监视器。因此,您应该将调用wait()对象的代码块声明为synchronized. 所以你的方法

public void foo(Observer o) 

应按以下方式定义:

public void foo(Observer o)
{
    Thread currentThread = Thread.currentThread();
    o.setThread(currentThread);
    // sleep until the observer wakes it
    synchronized(currentThread)
    {
      currentThread.wait(2000);   
    }
 }

更新:
按照您的要求,我建议您调用waitobject Observer。所以你的代码foo应该是这样的:

public void foo(Observer o)
{
    synchronized(o)
    {
      o.wait();//Don't pass time as parameter. Let only the Observer object to wake it up.
    }
 }

你的Observer 类应该这样定义:

public class Observer
{
    // ... other code  ....

   /*public void setThread(Thread t)
   {
       currentThread = t;
   }*/
   public synchronized void update(Observable o)
   {
        notify();//Will wake up the Thread waiting on the current Object of Observer
   }
}
于 2013-04-08T19:06:46.900 回答
2

这与这个问题不同。我不能将同步放在 foo 中,因为这会导致线程永远休眠。

我认为您不了解如何wait()工作notify()。您无需线程上等待和通知,而是在同一个对象上进行。当您的代码执行以下操作时:

currentThread.wait(2000);

它实际上导致当前线程等待它自己的Thread对象。通往notify()该线程的方式将类似于:

Thread thread = new Thread(myRunnable);
...
thread.notify();

这是一个非常奇怪的模式,很可能不是您想要做的。哪个线程正在运行该foo()方法并不重要。如果您使用的是线程池,您甚至不会知道哪个线程正在运行它。

如果您希望您的Observer线程通知等待的线程,foo()那么它们都需要使用同一个锁对象。就像是:

class MyClass {
    ...
    public synchronized void foo() {
        // this is waiting on the current instance of MyClass
        wait(2000);
    }
}

public class Observer {
     ...
     // to wake up the other thread, we lock the same myClass instance
     synchronized (myClass) {
         // and notify that object
         myClass.notify();
     }
}

或者你可以创建一个他们都应该共享的锁对象。

final Object lockObject = new Object();
MyClass c1 = new MyClass(lockObject);
Observer obs = new Observer(lockObject();
...
class MyClass {
    private final Object lockObject;
    public MyClass(Object lockObject) {
        this.lockObject = lockObject;
    }
    ...
    public void foo() {
        // this is waiting on the current instance of MyClass
        synchronized (lockObject) {
            lockObject.wait(2000);
        }
    }
}
...
public class Observer {
    private final Object lockObject;
    public Observer(Object lockObject) {
        this.lockObject = lockObject;
    }
    public void update(Observable o) {
        synchronized (lockObject) {
            lockObject.notify();
        }
    }
}

仅供参考:锁定对象应该几乎总是这样final,所以它们不能更改它们的引用。你永远不想锁定像 anInteger或 a那样变化的东西Boolean

于 2013-04-08T19:39:00.403 回答
1

我宁愿不要使用wait,或者notify因为它是低级别的,如果没有很好地实施,很快就会变脏。它可以通过二进制信号量来解决。

Semaphore sem = new Semaphore(0);
public void foo(Semaphore f){
    f.acquire();
}

其他线程,稍后可以调用f.release这将解除阻塞其他线程

于 2013-04-08T19:11:00.887 回答
0

您可以使用同步的。当您调用 currentThread.wait() 时,线程将休眠并释放监视器 (currentThread)。该线程将在等待被另一个线程在同一监视器上使用 notify() 唤醒之后。

于 2013-04-08T19:25:21.283 回答
0

要使用java.lang.Object.wait()你必须拥有监视器,它通常由同步块完成:

public void foo( Observer o ) throws InterruptedException
{
   Thread currentThread = Thread.currentThread();
   o.setThread( currentThread );
   synchronized( currentThread ) {
      currentThread.wait( 2000 );
   }
}

在 notify() 周围添加相同的机制

public void update( Observable o ) throws InterruptedException {
   synchronized( currentThread ) {
      currentThread.notify();
   }

}

于 2013-04-08T19:06:37.460 回答