代码之家  ›  专栏  ›  技术社区  ›  Bill K

有人能解释线程监视器并等待吗?

  •  37
  • Bill K  · 技术社区  · 17 年前

    工作中的某个人只是要求在同步器中包含等待的理由。

    老实说,我看不出原因。我理解javadocs所说的——线程需要是对象监视器的所有者,但为什么呢?它能防止什么问题?(如果确实有必要,为什么Wait方法不能获得监视器本身?)

    我正在寻找一个相当深入的原因,或者一篇文章的参考。我在一个快速的谷歌里找不到。

    哦,还有,thread.sleep比较起来怎么样?

    编辑:一套很好的答案——我真的希望我能选择多个答案,因为它们都能帮助我理解发生了什么。

    6 回复  |  直到 17 年前
        1
  •  4
  •   64BitBob    17 年前

    如果对象在调用object.wait()时不拥有对象监视器,则在释放监视器之前,它将无法访问该对象以设置通知侦听器。相反,它将被视为试图访问同步对象上的方法的线程。

    或者换一种说法,两者没有区别:

    public void doStuffOnThisObject()
    

    以及以下方法:

    public void wait()
    

    两种方法都将被阻止,直到对象监视器被释放。这是Java中的一个特性,用于防止对象被多个线程更新的状态。它只会对wait()方法产生意外的结果。

    假设wait()方法没有同步,因为这可能会导致线程在对象上拥有多个锁的情况。(参见 Java Language Specifications/Locking 有关这方面的详细信息。)多个锁是一个问题,因为wait()方法将只撤消一个锁。如果方法是同步的,它将保证只有方法的锁会被撤销,同时仍然会保留一个潜在的外部锁。这将在代码中创建死锁条件。

    为了回答您在thread.sleep()上的问题,thread.sleep()不能保证满足您等待的任何条件。使用object.wait()和object.notify()可以让程序员手动实现阻塞。一旦发送满足条件的通知,线程将取消阻止。例如,从磁盘读取已完成,线程可以处理数据。thread.sleep()要求程序员轮询是否满足条件,如果不满足条件则返回睡眠状态。

        2
  •  14
  •   Alex Miller    17 年前

    这里已经有很多好的答案了。但是这里要提到的是,另一个在使用wait()时必须做的是在一个依赖于您所等待的条件的循环中完成它,以防您看到虚假的唤醒,这在我的经验中确实发生过。

    要等待其他线程将条件更改为true并通知:

    synchronized(o) {
      while(! checkCondition()) {
        o.wait();
      }
    }
    

    当然,现在,我建议您只使用新的Condition对象,因为它更清晰并且具有更多的特性(例如,允许每个锁有多个条件,能够检查等待队列长度,更灵活的调度/中断等)。

     Lock lock = new ReentrantLock();
     Condition condition = lock.newCondition();
     lock.lock();
     try {
       while (! checkCondition()) {
         condition.await();
       }
     } finally {
       lock.unlock();
     }
    

    }

        3
  •  5
  •   Robin    17 年前

    它需要拥有监视器,因为wait()的目的是释放监视器,并让其他线程获得监视器来进行自己的处理。这些方法(wait/notify)的目的是协调两个线程之间对同步代码块的访问,这些线程需要彼此执行某些功能。不仅要确保对数据结构的访问是线程安全的,还要协调多个线程之间的事件。

    一个典型的例子是生产者/消费者案例,其中一个线程将数据推送到队列,另一个线程使用数据。消耗线程将始终要求监视器访问队列,但在队列为空时将释放监视器。当使用者不再处理时,生产者线程将只获得对该线程的写访问权。一旦将更多的数据推送到队列中,它就会通知使用者线程,这样它就可以重新获得监视器并再次访问队列。

        4
  •  5
  •   Lou Franco    17 年前

    等等,放弃显示器,所以你必须让它放弃。通知也必须有监视器。

    之所以要这样做,主要是为了确保从wait()返回时拥有监视器——通常,您使用wait/notify协议来保护某些共享资源,并且希望在wait返回时能够安全地触摸它。与notify相同——通常您正在更改一些内容,然后调用notify()——您希望拥有监视器、进行更改,并调用notify()。

    如果你做了这样的功能:

    public void synchWait() {
       syncronized { wait(); }
    }
    

    当wait返回时,您将没有监视器——您可以得到它,但下一步可能不会得到它。

        5
  •  3
  •   Mr Fooz    17 年前

    下面是我对为什么限制实际上是一个需求的理解。我把它基于一个C++监视器实现,我通过组合互斥体和条件变量做了一段时间。

    在一个 mutex+条件变量=监视器 系统 wait 调用将条件变量设置为等待状态并释放互斥体。条件变量是共享状态,因此需要锁定它,以避免希望等待的线程和希望通知的线程之间的争用条件。而不是引入另一个互斥锁来锁定其状态,而是使用现有的互斥锁。在Java中,当等待线程拥有监视器时,互斥锁被正确地锁定。

        6
  •  3
  •   Ram    16 年前

    如果有一个条件,比如队列是空的,那么通常会等待。

    If(queue is empty)
         queue.wait();
    

    假设队列是空的。 如果当前线程在检查队列之后先发制人,那么如果另一个线程 线程向队列添加了一些元素,当前线程将不知道,并将等待 状态。错了。 所以我们应该有

    Synchornized(queue)
    {
       if(queue is empty)
              queue.wait();
    }
    

    现在让我们考虑一下,如果它们使wait本身保持同步会怎么样。正如其中一条注释中所提到的,它只释放一个锁。这意味着如果在上面的代码中同步wait(),那么只会释放一个锁。意味着当前线程将等待队列的锁。