这两个陈述是否相等?
Thread.sleep(0);
Thread.yield();
Answers:
否。最明显的区别是sleep()
抛出(已检查)InterruptedException
。实际上,效果可能几乎相同,但完全取决于实现。
我敢打赌,在连续做各一百万次将采取很多更长的睡眠(),因为系统计时器粒度可能经常导致它实际上是一段不可忽略量的睡眠。
Thread.sleep(0)
和Thread.yield()
?难道根本就Thread.sleep(0)
没有睡眠吗?如果答案是肯定的,那么如何将其等同于Thread.yield()
仅向OS调度程序发送信号以调度其他线程?
sleep(0)
真正表示“根本不睡觉”,将需要额外的逻辑使其成为无操作,而不是像其他任何数字一样对待0,在这种情况下,这意味着“立即入睡并立即醒来”-这也可以让操作系统安排其他线程。由于专门处理这种极端情况并没有明显的好处,所以我希望大多数实现者不要这样做。
sleep(0)
可能会睡几十毫秒。
这实际上取决于JVM的平台和版本。例如,在Windows中,在JDK 5(Hotspot)中,yield()实际上被实现为Sleep(0)-尽管我记得Windows会稍微将sleep设为0。但是在JDK 6中,yield()被实现为SwitchToThread()。
不久前,我在Thread.yield()上汇总了一些信息,包括一些可能感兴趣的实现细节。(您可能还想查看我在同一站点上放在一起的Thread.sleep()上的内容。)
OpenJDK源码(Java SE 7中)有以下实施Thread.sleep(0)
在JVM_Sleep
jvm.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行代码。
yield()告诉JVM线程调度程序可以给其他线程时间片。通常,JVM使用此调用来激活具有相同线程优先级的另一个线程。在良好的抢占式多线程环境中,yield()是禁止操作的。但是,在协作式多线程环境中,这很重要,因为如果没有yield(),一个线程可能会耗尽所有CPU。
sleep(x)告诉JVM线程调度程序主动将该线程置于睡眠状态,直到至少x毫秒过去才再次运行该线程。
sleep()和yield()都不会更改有关同步锁状态的任何信息。如果您的线程有一个锁,并且您调用sleep(1000),则至少要经过一秒钟之后,线程才能唤醒。唤醒时,它可能会决定释放锁-或将其保持更长的时间。
Thread.Yield可以将CPU资源分配给优先级较低的线程,而Thread.Sleep(0)仅将CPU资源分配给优先级相同或更高的线程。
至少在Windows平台上:)
Thread.sleep()和Thread.yield()做相同的事情,除了Thread.yield()只放弃在多处理器环境中在同一处理器上运行的线程。
Thread.Sleep()
开销稍大,因为它创建的系统包含某种将唤醒进程的计时器。(基本上取决于实现)
底线它将最终调用a Yield()
。
Thread.Yield()
只会放弃线程的轮到,而在下一轮中获得它。
Thread.Sleep(0)
可能有一个优化方法,可以只调用yield。(再次执行)
yield()应该做的是使当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程轮流使用。因此,其目的是使用yield()来促进等优先级线程之间的顺畅转向。但是,实际上,并不能保证yield()方法可以实现它所声称的功能,即使yield()确实导致线程退出运行并返回可运行状态,也无法保证yield线程不会仅仅再次被选中!因此,尽管yield()可能而且通常会使运行中的线程将其插槽放弃给另一个具有相同优先级的可运行线程,但并不能保证。
yield()永远不会导致线程进入等待/睡眠/阻塞状态。最多,yield()会导致线程从运行状态变为可运行状态,但同样,它可能根本没有效果。
资料来源:SCJP Sun认证程序员手册
它依赖于平台和实现,并且它们可能不相等。
下面的代码片段在使用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();
}
不,它们不是等效的,除了上面的解释之外,我认为有必要检查的Javadoc yield
。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.