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

75

这两个语句是等效的吗?

Thread.sleep(0);
Thread.yield();

即使它们可能相等,使用yield()而不是sleep(0)总是更好的选择。 - Denis Tulskiy
12个回答

54

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

我敢打赌,连续执行一百万次,sleep()所需的时间会更长,因为系统计时器粒度可能经常导致它实际上要睡眠一段不可忽略的时间。


这是一个很好的观点,类似于sleep(1)可能会由于粒度而花费比1毫秒更长的时间。 - z -
2
@Michael 你写道:“实际上,效果可能几乎相同”。。。通过阅读Javadocs,我不清楚问题本身的相关性,并且无法理解在实践中,两个调用Thread.sleep(0)Thread.yield()的效果如何“可能”相同? Thread.sleep(0)难道不意味着根本没有休眠吗?如果答案是肯定的,那么它怎么能等同于Thread.yield(),后者只是向操作系统调度程序发送信号以安排其他线程? - Geek
3
@Geek: 要想让 sleep(0) 实际上意味着“根本不睡眠”,需要额外的逻辑将其变成无操作,而不是像处理其他数字一样将 0 视为“进入睡眠并立即唤醒”- 这也允许操作系统调度其他线程。由于在处理这种边缘情况时没有明显的好处,我预计大多数实现者不会这么做。 - Michael Borgwardt
@MichaelBorgwardt,我现在明白问题的要点了。感谢您的精彩回答,给您点赞+1。 - Geek
2
把0视为任何其他数字可能不意味着“...并立即再次唤醒”。更有可能意味着“进入调度程序堆栈的顶部,并在那里等待,在下一个调度程序心跳滴答声时变得可运行”。如果将零视为任何其他数字,则sleep(0)可能会睡眠数十毫秒。 - Solomon Slow

34

Yield会将当前线程添加到就绪队列中,以便其他线程可以运行。Sleep不能保证放弃CPU。


1
我认为这更多取决于平台。请看下面我的帖子。 - Neil Coffey
1
"yield()"并不保证让出CPU,但是一个不会使二者都不放弃CPU的操作系统是难以想象的。 - user207421

34

这实际上取决于平台和JVM的版本。例如,在JDK 5(Hotspot)中的Windows下,yield()被字面上实现为Sleep(0) - 尽管我回忆起一个0的睡眠在Windows上会稍微特殊处理。但是在JDK 6中,yield()被实现为SwitchToThread()。

我之前整理了一些关于Thread.yield()的信息,包括一些可能感兴趣的实现细节。(您还可以查看我在同一网站上整理的有关Thread.sleep()的内容。)


17

OpenJDK源代码(Java SE 7)在jvm.cpp文件的JVM_Sleep函数中实现了Thread.sleep(0)

  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::sleepos::yield是特定于平台的内容。 在Linux和Windows上: os::yield似乎要比os::sleep简单得多。 例如:Linux中的os::yield仅调用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线程调度器”的部分已经虚构了很多年。 - user207421

9
著名的Brian Goetz的书《Java并发编程实践》(2006年出版,但仍然基本有效)对这个问题有如下说明。
“Thread.yield”和“Thread.sleep(0)”的语义是未定义的[JLS17.9];JVM可以将它们实现为无操作或将它们视为调度提示。特别是,在Unix系统上,它们不需要具有sleep(0)的语义-将当前线程放在该优先级的运行队列的末尾,让给相同优先级的其他线程-尽管一些JVM以这种方式实现yield。
其余内容可以在Javadoc页面中找到。

3
它依赖于平台和实现,它们可能不相等。
在使用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();
}

1

Thread.Yield可以将CPU资源让给优先级更低的线程,而Thread.Sleep(0)只会将CPU让给优先级相等或更高的线程。

至少在Windows平台上是这样的 :)


这与事件调整线程优先级以抵消优先级反转的方式有关吗? - finnw
https://dev59.com/p1XTa4cB1Zd3GeqPzTa8#8274138 似乎与您(关于yielding)持不同意见。谁是正确的? - Pacerier
这些都不是Java规范所要求的。 - user207421

0
不,它们并不等价,除了上面的解释之外, 我认为有必要检查 yield 的 Javadoc。除非满足以下情况,否则使用 yield 不是一个好主意。
 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.

0

Thread.sleep()Thread.yield() 做的事情是一样的,只不过在多处理器环境中,Thread.yield() 只会让运行在同一处理器上的线程放弃执行。


1
那个声明的来源是什么? - Oleg Estekhin
3
这是在.NET中的情况,我不确定Java是否也适用。https://msdn.microsoft.com/zh-cn/library/system.threading.thread.yield(v=vs.110).aspx - Abdulrahman Awwad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接