java 谁能解释线程监视器并等待?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/226455/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 11:31:22  来源:igfitidea点击:

Can anyone explain thread monitors and wait?

javamultithreadingmonitor

提问by Bill K

Someone at work just asked for the reasoning behind having to wrap a wait inside a synchronized.

工作中的某人只是询问必须将等待包装在同步中的原因。

Honestly I can't see the reasoning. I understand what the javadocs say--that the thread needs to be the owner of the object's monitor, but why? What problems does it prevent? (And if it's actually necessary, why can't the wait method get the monitor itself?)

老实说,我看不出其中的道理。我理解 javadocs 所说的——线程需要是对象监视器的所有者,但为什么呢?它可以防止什么问题?(如果真的有必要,为什么wait方法不能自己获取监视器呢?)

I'm looking for a fairly in-depth why or maybe a reference to an article. I couldn't find one in a quick google.

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

Oh, also, how does thread.sleep compare?

哦,还有,thread.sleep 比较如何?

edit: Great set of answers--I really wish I could select more than one because they all helped me understand what was going on.

编辑:很棒的一组答案——我真的希望我可以选择多个答案,因为它们都帮助我了解正在发生的事情。

采纳答案by 64BitBob

If the object does not own the object monitor when it calls Object.wait(), it will not be able to access the object to setup a notify listener until the the monitor is released. Instead, it will be treated as a thread attempting to access a method on a synchronized object.

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

Or to put it another way, there is no difference between:

或者换句话说,两者之间没有区别:

public void doStuffOnThisObject()

and the following method:

以及以下方法:

public void wait()

Both methods will be blocked until the object monitor is released. This is a feature in Java to prevent the state of an object from being updated by more than one thread. It simply has unintended consequences on the wait() method.

这两种方法都将被阻塞,直到对象监视器被释放。这是 Java 中的一项功能,用于防止对象的状态被多个线程更新。它只是对 wait() 方法产生了意想不到的后果。

Presumably, the wait() method is not synchronized because that could create situations where the Thread has multiple locks on the object. (See Java Language Specifications/Lockingfor more info on this.) Multiple locks are a problem because the wait() method will only undo one lock. If the method were synchronized, it would guarantee that only the method's lock would be undone while still leaving a potential outer lock undone. This would create a deadlock condition in the code.

据推测,wait() 方法未同步,因为这可能会造成 Thread 对对象有多个锁的情况。(有关这方面的更多信息,请参阅Java 语言规范/锁定。)多锁是一个问题,因为 wait() 方法只会撤消一个锁。如果方法是同步的,它将保证只有方法的锁会被撤消,同时仍然保留潜在的外部锁被撤消。这会在代码中产生死锁条件。

To answer your question on Thread.sleep(), Thread.sleep() does not guarantee that whatever condition you are waiting on has been met. Using Object.wait() and Object.notify() allows a programmer to manually implement blocking. The threads will unblock once a notify is sent that a condition has been met. e.g. A read from disk has finished and data can be processed by the thread. Thread.sleep() would require the programmer to poll if the condition has been met, then fall back to sleep if it has not.

要回答您关于 Thread.sleep() 的问题,Thread.sleep() 不保证您正在等待的任何条件都已满足。使用 Object.wait() 和 Object.notify() 允许程序员手动实现阻塞。一旦发送通知满足条件,线程将解除阻塞。例如,从磁盘读取已完成,线程可以处理数据。Thread.sleep() 将要求程序员轮询是否满足条件,如果未满足则返回睡眠状态。

回答by Alex Miller

Lots of good answers here already. But just want to mention here that the other MUST DO when using wait() is to do it in a loop dependent on the condition you are waiting for in case you are seeing spurious wakeups, which in my experience do happen.

这里已经有很多好的答案了。但在这里只想提一下,在使用 wait() 时,另一个必须做的事情是根据您正在等待的条件在循环中执行此操作,以防您看到虚假唤醒,根据我的经验,这种情况确实会发生。

To wait for some other thread to change a condition to true and notify:

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

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

Of course, these days, I'd recommend just using the new Condition object as it is clearer and has more features (like allowing multiple conditions per lock, being able to check wait queue length, more flexible schedule/interrupt, etc).

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

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

}

}

回答by Robin

It needs to own the monitor, since the purpose of the wait() is to release the monitor and let other threads obtain the monitor to do processing of their own. The purpose of these methods (wait/notify) is to coordinate access to synchronized code blocks between two threads that require each other to perform some functionality. It is not simply a matter of making sure access to a data structure is threadsafe, but to coordinate events between multiple threads.

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

A classic example would be a producer/consumer case where one thread pushes data to a queue, and another thread consumes the data. The consuming thread would always require the monitor to access the queue, but would release the monitor once the queue is empty. The producer thread would then only get access to write to the thread when the consumer is no longer processing. It would notify the consumer thread once it has pushed more data into the queue, so it can regain the monitor and access the queue again.

一个典型的例子是生产者/消费者的情况,其中一个线程将数据推送到队列,另一个线程使用数据。消费线程总是需要监视器访问队列,但一旦队列为空就会释放监视器。生产者线程只有在消费者不再处理时才能访问写入线程。一旦将更多数据推送到队列中,它就会通知消费者线程,因此它可以重新获得监视器并再次访问队列。

回答by Lou Franco

Wait gives up the monitor, so you must have it to give it up. Notify must have the monitor as well.

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

The main reason why you want to do this is to ensure that you have the monitor when you come back from wait() -- typically, you are using the wait/notify protocol to protect some shared resource and you want it to be safe to touch it when wait returns. The same with notify -- usually you are changing something and then calling notify() -- you want to have the monitor, make changes, and call notify().

您要这样做的主要原因是确保在您从 wait() 返回时拥有监视器——通常,您使用等待/通知协议来保护某些共享资源,并且您希望它是安全的等待返回时触摸它。与notify 一样——通常你正在改变一些东西然后调用notify()——你想要有监视器,进行更改,然后调用notify()。

If you made a function like this:

如果你做了一个这样的函数:

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

You would not have the monitor when wait returned -- you could get it, but you might not get it next.

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

回答by Mr Fooz

Here's my understanding on why the restriction is actually a requirement. I'm basing this on a C++ monitor implementation I made a while back by combining a mutex and a condition variable.

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

In a mutex+condition_variable=monitorsystem, the waitcall sets the condition variable into a wait state and releases the mutex. The condition variable is shared state, so it needs to be locked to avoid race conditions between threads that want to wait and threads that want to notify. Instead of introducing yet another mutex to lock its state, the existing mutex is used. In Java, the mutex is correctly locked when the about-to-wait thread owns the monitor.

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

回答by Mr Fooz

Mostly wait is done if there is a condition say a queue is empty.

如果有条件说队列为空,则主要等待完成。

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

Let us assume the queue is empty. In case if the current thread pre-empts after checking the queue, then if another thread adds few elements to queue, the current thread will not know and will go for wait state. Thats wrong. So we should have something like

让我们假设队列是空的。如果当前线程在检查队列后抢占,那么如果另一个线程向队列添加很少的元素,当前线程将不知道并进入等待状态。那是错误的。所以我们应该有类似的东西

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

Now let us consider what if they made wait itself as synchronized. As already mentioned in one of the comments, it releases only one lock. That means if wait() was synchronized in the above code only one lock would have been released. Implies that current thread will go for wait with the lock for the queue.

现在让我们考虑一下,如果他们将等待本身设为同步会怎样。正如在其中一条评论中已经提到的,它只释放一个锁。这意味着如果上面代码中的 wait() 是同步的,那么只会释放一个锁。暗示当前线程将使用队列的锁等待。