Thread.sleep(0)和Thread.yield()语句是否等效?


Answers:


48

否。最明显的区别是sleep()抛出(已检查)InterruptedException。实际上,效果可能几乎相同,但完全取决于实现。

我敢打赌,在连续做各一百万次将采取很多更长的睡眠(),因为系统计时器粒度可能经常导致它实际上是一段不可忽略量的睡眠。


这是一个好主意,由于粒度的原因,诸如sleep(1)之类的时间可能会长于1毫秒。
z-

2
@迈克尔你写“在实践中,效果可能差不多了”。通过阅读的Javadoc我不清楚有关问题本身的相关性和无法理解 在实践中的效果如何可能是两个呼叫的同Thread.sleep(0)Thread.yield()?难道根本就Thread.sleep(0)没有睡眠吗?如果答案是肯定的,那么如何将其等同于Thread.yield()仅向OS调度程序发送信号以调度其他线程?
极客

3
@Geek:要sleep(0)真正表示“根本不睡觉”,将需要额外的逻辑使其成为无操作,而不是像其他任何数字一样对待0,在这种情况下,这意味着“立即入睡并立即醒来”-这可以让操作系统安排其他线程。由于专门处理这种极端情况并没有明显的好处,所以我希望大多数实现者不要这样做。
Michael Borgwardt 2013年

@MichaelBorgwardt我现在明白了问题的重点。感谢+1为您的精彩回答。
极客2013年

2
像其他任何数字一样对待0可能并不意味着“ ...然后立即再次醒来”。它更可能将意味着,“去调度的堆的顶部,并在那里等候,成为可运行在下一调度心跳打勾。 如果为零就像任何其他数字处理,然后sleep(0)可能会睡几十毫秒。
所罗门慢

33

Yield将当前线程添加到就绪队列中,并允许其他线程运行。不能保证睡眠可以放弃CPU。


1
我认为这实际上更依赖于平台。请参阅下面的我的帖子。
尼尔·科菲

yield()不能保证也不能让CPU退出,但是不会导致两者都放弃CPU的操作是很难想象的。
2015年

31

这实际上取决于JVM的平台和版本。例如,在Windows中,在JDK 5(Hotspot)中,yield()实际上被实现为Sleep(0)-尽管我记得Windows会稍微将sleep设为0。但是在JDK 6中,yield()被实现为SwitchToThread()。

不久前,我在Thread.yield()汇总了一些信息,包括一些可能感兴趣的实现细节。(您可能还想查看我在同一站点上放在一起的Thread.sleep()上的内容。)


14

OpenJDK源码(Java SE 7中)有以下实施Thread.sleep(0)JVM_Sleepjvm.cpp的功能:

  if (millis == 0) {
    // When ConvertSleepToYield is on, this matches the classic VM implementation of
    // JVM_Sleep. Critical for similar threading behaviour (Win32)
    // It appears that in certain GUI contexts, it may be beneficial to do a short sleep
    // for SOLARIS
    if (ConvertSleepToYield) {
      os::yield();
    } else {
      ThreadState old_state = thread->osthread()->get_state();
      thread->osthread()->set_state(SLEEPING);
      os::sleep(thread, MinSleepInterval, false);
      thread->osthread()->set_state(old_state);
    }
  }

Thread.yield()的实现具有以下代码:

  // When ConvertYieldToSleep is off (default), this matches the classic VM use of yield.
  // Critical for similar threading behaviour
  if (ConvertYieldToSleep) {
    os::sleep(thread, MinSleepInterval, false);
  } else {
    os::yield();
  }

因此Thread.sleep(0)Thread.yield()在某些平台上可能会调用相同的系统调用。

os::sleep并且os::yield是平台特定的东西。在Linux和Windows上:os::yield似乎比简化得多os::sleep。例如:os::yield仅Linux调用sched_yield()。并os::sleep有大约70行代码。


10

yield()告诉JVM线程调度程序可以给其他线程时间片。通常,JVM使用此调用来激活具有相同线程优先级的另一个线程。在良好的抢占式多线程环境中,yield()是禁止操作的。但是,在协作式多线程环境中,这很重要,因为如果没有yield(),一个线程可能会耗尽所有CPU。

sleep(x)告诉JVM线程调度程序主动将该线程置于睡眠状态,直到至少x毫秒过去才再次运行该线程。

sleep()和yield()都不会更改有关同步锁状态的任何信息。如果您的线程有一个锁,并且您调用sleep(1000),则至少要经过一秒钟之后,线程才能唤醒。唤醒时,它可能会决定释放锁-或将其保持更长的时间。

消息来源:http://www.jguru.com/faq/view.jsp?EID = 425624


2
非规范性参考。其中大部分不在Javadoc中,有关“ JVM Thread Scheduler”的部分已经有很多年了。
user207421

7

著名的Brian Goetz的书“ Java Concurrency in Practice”(于2006年出版,但从根本上来说仍然有效)对这个问题作了以下说明。

Thread.yield和Thread.sleep(0)的语义未定义[JLS17.9]; JVM可以自由地将它们实现为无操作,也可以将它们视为调度提示。特别是,在Unix系统上,它们不需要具有sleep(0)的语义-将当前线程置于该优先级的运行队列的末尾,从而产生具有相同优先级的其他线程-尽管某些JVM在这条路。

其余的可以在Javadoc页面中找到。




0

Thread.Sleep()开销稍大,因为它创建的系统包含某种将唤醒进程的计时器。(基本上取决于实现)
底线它将最终调用a Yield()

Thread.Yield() 只会放弃线程的轮到,而在下一轮中获得它。

Thread.Sleep(0)可能有一个优化方法,可以只调用yield。(再次执行)


您说睡觉的开销很大。您在谈论哪个操作系统?
Pacerier 2012年

0

yield()应该做的是使当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程轮流使用。因此,其目的是使用yield()来促进等优先级线程之间的顺畅转向。但是,实际上,并不能保证yield()方法可以实现它所声称的功能,即使yield()确实导致线程退出运行并返回可运行状态,也无法保证yield线程不会仅仅再次被选中!因此,尽管yield()可能而且通常会使运行中的线程将其插槽放弃给另一个具有相同优先级的可运行线程,但并不能保证。

yield()永远不会导致线程进入等待/睡眠/阻塞状态。最多,yield()会导致线程从运行状态变为可运行状态,但同样,它可能根本没有效果。

资料来源:SCJP Sun认证程序员手册


某种不连贯的非规范性参考。如果没有“保证”去做某事,怎么可能“去做”呢?
user207421

0

它依赖于平台和实现,并且它们可能不相等。

下面的代码片段在使用Thread.sleep(0)时,大部分时间提供输出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

而使用Thread.yield()时,主要给出:

[0, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[0, 2, 2, 2, 2, 2, 2, 2, 2, 2]

请参见下面的代码段:

public class CompareSleepZeroAndYield {
    private ArrayList<Integer> list1 = new ArrayList<>();
    private ArrayList<Integer> list2 = new ArrayList<>();

    public ArrayList<Integer> getList1() {
        return list1;
    }

    public ArrayList<Integer> getList2() {
        return list2;
    }

    public CompareSleepZeroAndYield() {
        list1.add(0);
        list2.add(0);
    }

    public void tryFieldLock1() {
        synchronized (this.list1) {
            list1.add(list2.get(list2.size() - 1) + 1);
        }
    }

    public void tryFieldLock2() {
        synchronized (this.list2) {
            list2.add(list1.get(list1.size() - 1) + 1);
        }
    }

    public static void main(String[] args) {
        CompareSleepZeroAndYield obj = new CompareSleepZeroAndYield();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                int count = 10;
                while (--count >0) {
                    obj.tryFieldLock1();
                    try {
                        Thread.sleep(0);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // compare above and below
                    // Thread.yield()
                }
                System.out.println(obj.getList1());
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                int count = 10;
                while (--count >0) {
                    obj.tryFieldLock2();

                    try {
                        Thread.sleep(0);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // compare above and below
                    // Thread.yield()
                }
                System.out.println(obj.getList2());
            }
        });
        t1.start();
        t2.start();
}

0

不,它们不是等效的,除了上面的解释之外,我认为有必要检查的Javadoc yieldyield除非遇到以下情况,否则使用它似乎不是一个好主意。

 It is rarely appropriate to use this method. It may be useful
 for debugging or testing purposes, where it may help to reproduce
 bugs due to race conditions. It may also be useful when designing
 concurrency control constructs such as the ones in the
 {@link java.util.concurrent.locks} package.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.