在wait()之前调用Java notify()

时间:2012-05-01 08:52:35

标签: java multithreading wait notify

是否可能在一个线程中的wait()之前调用另一个线程中的notify()? 它发生在我身上。

客户端从目标请求值并等待结果变量RV。 如果目标是客户端本身,我使用正确的结果更新RV,并在另一个线程中调用RV上的notify()。

class EMU {

  ResultVar RV;
  Address my_address;

  ResultVar findValue(String key) {
    String tgt = findTarget(key);
    sendRequest(tgt, key);
    synchronized(RV) {
      RV.wait();
    }

    return RV;
  }

  Runnable Server = new Runnable() {
    public void run() {
      //code to receive connections. Assume object of type Request is read from the stream.
      Request r = (Request) ois.readObject();
      if(r.requesterAddr.compareTo(my_address) == 0) {
        String val = findVal(key);
        RV.putVal(val);
        synchronized(RV){
          RV.notify();
        }
      }
    }
  };
}

问题是在请求者自己完成所有“网络”(上例中的sendReqest)之前,结果会在结果变量中更新。当请求者线程现在调用wait()时,程序不会继续,因为已经调用了notify。

我们如何防止它?

5 个答案:

答案 0 :(得分:7)

在等待之前检查一些标志(在循环中),请参阅教程:http://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

答案 1 :(得分:5)

在没有被另一个帖子notify编辑的对象上调用wait时,没有任何阻止。

听起来你想要的只是在某些条件成立时等待。例如:

synchronized (results) {
    while (!results.hasResults()) {
        // no results yet; wait for them
        try {
            results.wait();
        } catch (InterruptedException ie) { /* ignore */ }
    }
}

答案 2 :(得分:2)

我强烈建议不要重新发明轮子。

Java Future接口专为可能只在以后到达的结果而设计,FutureTask类实现此接口。

让第一个线程获得对Future的访问权并获得第二个线程来运行FutureTask,并为您处理所有这些内容。您还可以免费获得超时支持。

答案 3 :(得分:0)

在转到wait()之前使用某些条件并确保条件是线程安全的:)

class EMU{
    ResultVar RV;
    Address my_address;
    volatile boolean condition = true;

    ResultVar findValue(String key){
        String tgt = findTarget(key);
        sendRequest(tgt, key);
        synchronized(RV){
            while(condition == true)
            {
                RV.wait();
            }
        }
        return RV;
    }

    Runnable Server = new Runnable(){
        public void run(){
            //code to receive connections. Assume object of type Request is read from the stream.
            Request r = (Request) ois.readObject();
            if(r.requesterAddr.compareTo(my_address) == 0){
                String val = findVal(key);
                RV.putVal(val);
                synchronized(RV){
                    condition = false;
                    RV.notify();
                }
            }
        }

    };

答案 4 :(得分:0)

让我首先将代码分解为可重复的最低代码:

public static void main(String[] args) throws Exception {
    Object RV = new Object();
    new Thread() {
        @Override
        public void run() {
            synchronized (RV) {
                RV.notify();
            }
        }
    }.start();
    Thread.sleep(1_000);
    synchronized (RV) {
        RV.wait();
    }
}

理论上该方法将永远不会结束,并且程序也永远不会退出。如果这是一个僵局,那将是一个争执。

我的解决方案是创建第二个锁:

public static void main(String[] args) throws Exception {
    Object RV = new Object();
    Object lock = new Object();
    new Thread() {
        @Override
        public void run() {
            synchronized (lock) {
                lock.wait();
            }
            synchronized (RV) {
                RV.notify();
            }
        }
    }.start();
    Thread.sleep(1_000);
    synchronized (RV) {
        synchronized (lock) {
            lock.notify();
        }
        RV.wait();
    }
}

让我们检查主线程等待一秒钟时线程在做什么:

  1. 自定义线程将首先加入synchronized(lock)块。
  2. 然后锁定将使自定义线程等待。
  3. 1秒钟后,主线程加入了RV同步。
  4. 该锁将得到通知,并导致自定义线程继续工作。
  5. 自定义线程离开synchronized(lock)块。
  6. 主线程将RV等待锁定。
  7. 自定义线程通知RV锁继续。

程序结束。