【问题标题】:difference between wait() and yield()wait() 和 yield() 的区别
【发布时间】:2014-11-07 09:30:11
【问题描述】:

到目前为止,我对wait() 和yield() 方法的理解是,当线程不执行任何任务并让CPU 执行其他线程时调用yield()。 wait() 在某些线程被搁置时使用,通常用于同步的概念。但是,我无法理解它们在功能上的区别,我不确定我所理解的是对还是错。有人可以解释一下它们之间的区别(除了它们所在的包)。

【问题讨论】:

  • 好吧,如果你.wait(),另一个线程必须.notify()你——在完全相同的对象引用上。 .yield() 只是对 JVM 的一个提示,“如果你想安排我出去,你暂时可以”
  • 那么他们不是都在做同样的任务——等待其他线程可以执行吗?还有,当使用yield()时,线程如何回到可运行状态?

标签: java multithreading


【解决方案1】:

他们不是都在做同样的任务——等待其他线程可以执行吗?

甚至没有接近,因为yield() 不会等待任何事情。

每个线程都可以处于多种不同状态中的一种:Running 意味着线程实际上正在 CPU 上运行,Runnable 意味着没有任何东西阻止线程除了运行之外,也许有一个 CPU 可以运行。所有其他状态都可以归为一个称为阻塞的类别。阻塞线程是指等待某事发生后才能运行的线程。

操作系统抢占定期运行线程:每隔一段时间(在大多数操作系统上,在每秒 10 次到每秒 100 次之间)操作系统标记每个正在运行的线程并说,“轮到你了,转到运行队列的后面”(即,将状态从运行更改为可运行)。然后它让运行队列头部的任何线程使用该 CPU(即再次运行)。

当你的程序调用Thread.yield()时,它对操作系统说,“我还有工作要做,但它可能不如其他线程正在做的工作重要。请把我送到后面现在运行队列。”如果有可用的 CPU 供线程运行,那么它实际上将继续运行(即,yield() 调用将立即返回)。

另一方面,当您的程序调用 foobar.wait() 时,它对操作系统说:“阻止我,直到其他线程调用 foobar.notify()

Yielding 最初是在非抢占式操作系统和非抢占式线程库中实现的。在只有一个 CPU 的计算机上,唯一多个线程必须运行的方式是当线程显式地相互让步时。

让步对于忙于等待也很有用。这就是线程通过坐在一个紧密的循环中等待某事发生的地方,一遍又一遍地测试相同的条件。如果条件依赖于某个其他线程来完成某些工作,则等待的线程每次在循环中都会 yield() 以让其他线程完成它的工作。

现在我们有了为我们提供更高级别同步对象的抢占式和多处理器系统和库,基本上没有理由再需要应用程序调用yield()

【讨论】:

  • 谢谢,这很有帮助:)
  • 每秒 100 次可能看起来很多,但如果您有两个高度相互依赖的线程,那么在每个循环中,yield 可能会为您节省 1/100 秒。例如,假设您尝试标记数组中的所有偶数,而其他线程对奇数执行相同操作,并且您不能允许它们的间隙大于 100。具有如此极端的依赖性, wait() 可能会导致性能显着下降,而产生的效果却不是很多。
  • @IlyaGazman,Thread.yield() 的 javadoc 清楚地表明它根本不需要做任何事情。您不能出于任何目的依赖它,因为当您调用它时,它实际上可能什么都不做
【解决方案2】:

wait 用于等待条件。在查看该方法时,这可能不会引起注意,因为完全由您来定义它是哪种条件。但是 API 试图通过要求您拥有正在等待的对象的监视器来强制您正确使用它,这对于在多线程环境中进行正确的条件检查是必要的。

所以wait 的正确用法如下:

synchronized(object) {
  while( ! /* your defined condition */)
    object.wait();
  /* execute other critical actions if needed */
}

它必须与另一个执行代码的线程配对,例如:

synchronized(object) {
  /* make your defined condition true */)
  object.notify();
}

相比之下,Thread.yield() 只是一个提示,您的线程可能会在此时释放 CPU。没有具体说明它是否真的做任何事情,而且不管 CPU 是否被释放,它对内存模型的语义没有影响。换句话说,它不会与正确访问共享变量所需的其他线程建立任何关系。

例如,以下访问sharedVariable(未声明为volatile)的循环可能会一直运行而不会注意到其他线程所做的更新:

while(sharedVariable != expectedValue) Thread.yield();

虽然Thread.yield 可能会帮助其他线程运行(它们在大多数系统上无论如何都会运行),但它确实不会强制从共享内存中重新读取sharedVariable 的值。因此,没有其他构造强制执行内存可见性,例如将sharedVariable 改成volatile,这个循环就坏了。

【讨论】:

  • 通常情况下,最好使用notifyAll() 而不是notify,因为不同的线程可以等待不同的条件发生,如果使用notify,它可能会转到错误的线程,并且该通知对于它真正属于的线程丢失。如果你知道等待线程不超过一个,使用notify 就可以了。
  • @Victor Sorokin:这显然取决于条件。例如。如果您将一个项目恰好放入一个空队列,则没有理由唤醒多个消费者。如果您有多种不同的条件,您可以考虑使用Lock,因为该API 支持定义不同的conditions,您可以使用awaitsignal
  • 好吧,你说得对,这里有明确的Condition,但不能说在有多个消费者的情况下使用notify 是安全的,在一般情况下 (例如,如果不同的消费者消费不同类型的商品怎么办?)。
  • @Victor Sorokin:在这种情况下,您不同的条件,因为您等待的不是“非空条件”而是“Foo 可用”和“Bar 可用”。在这种情况下,您需要消费者在没有消费时重新notify,或者生产者使用notifyAll,而前者可能很快就会变得比后者更加低效。没错,如果有疑问,只需使用notifyAll...
  • @VictorSorokin,而notifyAll 似乎是首选解决方案,当涉及多个线程时,它会对性能产生非常可怕的影响。所有线程都必须有效地获取监视器,从而导致大量的一致性流量和 CPU 浪费。话虽如此,如果同一个锁有不同的条件,您可能需要重构代码并使用更多锁。
【解决方案3】:

第一个区别是yield()Thread方法,wait()Object方法的起源thread中继承的所有类,在形状中,在后台(使用java文档)

wait()

使当前线程等待,直到另一个线程为此对象调用 notify() 方法或 notifyAll() 方法。换句话说,这个方法的行为就像它只是执行调用 wait(0) 一样。

yield()

向调度程序提示当前线程愿意让出其当前使用的处理器。调度程序可以随意忽略此提示。

在这里你可以看到yield()wait()之间的difference

【讨论】:

    猜你喜欢
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 2018-06-03
    • 1970-01-01
    • 2016-06-01
    • 2010-12-14
    • 1970-01-01
    相关资源
    最近更新 更多