【问题标题】:Java notify() gets called before wait()Java notify() 在 wait() 之前被调用
【发布时间】:2012-05-10 20:25:04
【问题描述】:

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

我们该如何预防?

【问题讨论】:

  • In case the target is the client itself 你是什么意思?你能放一些代码示例吗?
  • @shishir garg 你能粘贴一些代码吗
  • 没有看到代码很难回答 - 虽然有 2 个 cmets:a) 通常最好使用 notifyAll 而不是 notify,除非您知道自己在做什么 b) 可以使用 wait 和 notify容易出错,除非您需要非常具体的东西,否则您应该使用更高级别的并发 API。

标签: java multithreading wait notify


【解决方案1】:

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

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 继续。

程序结束。

【讨论】:

    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

        没有什么能阻止你在一个未被另一个线程waited 的对象上调用notify

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

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

        【讨论】:

          【解决方案5】:

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

          【讨论】:

            猜你喜欢
            • 2013-11-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多