5

在一个线程中的 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。

我们该如何预防呢?

4

5 回答 5

7

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

于 2012-05-01T09:08:53.057 回答
5

没有什么能阻止你调用notify一个未被wait另一个线程编辑的对象。

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

synchronized (results) {
    while (!results.hasResults()) {
        // no results yet; wait for them
        try {
            results.wait();
        } catch (InterruptedException ie) { /* ignore */ }
    }
}
于 2012-05-01T09:10:28.857 回答
2

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

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

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

于 2012-05-01T10:49:13.483 回答
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();
                }
            }
        }

    };
于 2013-11-28T15:18:14.580 回答
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-wait-lock。
  7. 自定义线程通知 RV-lock 继续。

程序结束。

于 2020-01-18T20:08:54.747 回答