Thread.sleep() 实现
今天我参加了一次面试,我向应聘者询问了有关 Thread.sleep() 和 Object.wait() 之间区别的常见且基本的问题。我希望他能回答类似这样的问题,但他说这些方法基本上是同样的事情,很可能 Thread.sleep
在其内部使用 Object.wait()
,但 sleep
本身不需要外部锁。这并不完全是正确的答案,因为在 JDK 1.6 中该方法具有以下签名。
public static native void sleep(long millis) throws InterruptedException;
但我的第二个想法是,这并没有那么荒谬。可以使用定时等待来达到相同的效果。看一下下面的代码片段:
public class Thread implements Runnable {
private final Object sleepLock = new Object();
// other implementation details are skipped
public static void sleep(long millis) throws InterruptedException {
synchronized (getCurrentThread().sleepLock){
getCurrentThread().sleepLock.wait(millis);
}
}
在本例中,sleepLock
是一个专门用于 sleep
方法内的同步块的对象。我假设 Sun/Oracle 工程师了解奥卡姆剃刀原理,因此 sleep
有意有本机实现,所以我的问题是为什么它使用本机调用。
我想到的唯一想法是假设有人可能会找到像 Thread.sleep(0) 这样有用的调用。根据这篇文章,调度程序管理是有意义的:
这具有清除当前线程的量程并将其放入优先级队列末尾的特殊效果。换句话说,所有具有相同优先级(以及更高优先级)的可运行线程将有机会在下一次给定 CPU 时间之前运行。
因此,同步块会带来不必要的开销。
您知道在 Thread.sleep()
实现中不使用定时等待的任何其他原因吗?
Today I had an interview on which I asked candidate quite usual and basic question about the difference between Thread.sleep()
and Object.wait()
. I expected him to answer something like like this, but he said these methods basically are the same thing, and most likely Thread.sleep
is using Object.wait()
inside it, but sleep
itself doesn't require external lock. This is not exactly a correct answer, because in JDK 1.6 this method have following signature.
public static native void sleep(long millis) throws InterruptedException;
But my second thought was that it's not that ridiculous. It's possible to use timed wait to achieve the same effect. Take a look at the following code snippet:
public class Thread implements Runnable {
private final Object sleepLock = new Object();
// other implementation details are skipped
public static void sleep(long millis) throws InterruptedException {
synchronized (getCurrentThread().sleepLock){
getCurrentThread().sleepLock.wait(millis);
}
}
In this case sleepLock
is an object which is used particularly for the synchronization block inside sleep
method. I assume that Sun/Oracle engineers are aware of Occam's razor, so sleep
has native implementation on purpose, so my question is why it uses native calls.
The only idea I came up with was an assumption that someone may find useful invocation like Thread.sleep(0)
. It make sense for scheduler management according to this article:
This has the special effect of clearing the current thread's quantum and putting it to the end of the queue for its priority level. In other words, all runnable threads of the same priority (and those of greater priority) will get a chance to run before the yielded thread is next given CPU time.
So a synchronized
block will give unnecessary overhead.
Do you know any other reasons for not using timed wait in Thread.sleep()
implementation?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
人们可以很容易地说奥卡姆剃刀是相反的。假定 JDK 底层的 JVM 的正常/预期实现在大多数情况下将 java“线程”绑定到本机线程,并且使线程休眠是底层平台的基本功能。如果线程代码无论如何都是本机的,为什么还要在 java 中重新实现它呢?最简单的解决方案是使用已有的功能。
其他一些考虑因素:
无竞争同步在现代 JVM 中可以忽略不计,但情况并非总是如此。获取该对象监视器曾经是一项相当“昂贵”的操作。
如果您在 Java 代码中实现线程休眠,并且实现它的方式并不绑定到本机线程等待,则操作系统必须继续调度该线程,以便运行检查是否到了唤醒时间的代码。正如评论中所阐述的那样,对于现代 JVM 上的示例来说,这显然是不正确的,但很难说
1) 首次以这种方式指定 Thread 类时可能已经存在和预期的内容。
和
2) 如果该断言适用于人们可能想要实现 JVM 的每个平台。
One could easily say Occam's Razor cuts the other way. The normal/expected implementation of the JVM underlying JDK is assumed to bind java 'threads' onto native threads most of the time, and putting a thread to sleep is a fundamental function of the underlying platform. Why reimplement it in java if thread code is going to be native anyway? The simplest solution is use the function that's already there.
Some other considerations:
Uncontested synchronization is negligible in modern JVMs, but this wasn't always so. It used to be a fairly "expensive" operation to acquire that object monitor.
If you implement thread sleeping inside java code, and the way you implement it does not also bind to a native thread wait, the operating system has to keep scheduling that thread in order to run the code that checks if it's time to wake up. As hashed out in the comments, this would obviously not be true for your example on a modern JVM, but it's tough to say
1) what may have been in place and expected at the time the Thread class was first specified that way.
and
2) If that assertion works for every platform one may have ever wanted to implement a JVM on.
因为本机线程库提供了完美的睡眠功能: http://www .gnu.org/software/libc/manual/html_node/Sleeping.html
要了解为什么本机线程很重要,请从 http://java.sun.com/docs/hotspot/threads/threads.html
Because the native thread libraries provide a perfectly good sleep function: http://www.gnu.org/software/libc/manual/html_node/Sleeping.html
To understand why native threads are important, start at http://java.sun.com/docs/hotspot/threads/threads.html
Thread.sleep() 不会被虚假唤醒提前唤醒。如果使用 Object.wait(),要正确执行此操作(即确保等待足够的时间),您将需要一个循环来查询已用时间(例如 System.currentTimeMillis()),以确保您等待足够的时间。
从技术上讲,您可以使用 Object.wait() 实现 Thread.sleep() 相同的功能,但您需要编写更多代码才能正确实现。
这也是一个相关且有用的讨论。
Thread.sleep() will not be woken up early by spurious wakeups. If using Object.wait(), to do it properly (i.e. ensure you wait enough time) you would need a loop with a query to elapsed time (such as System.currentTimeMillis()) to make sure you wait enough.
Technically you could achieve the same functionality of Thread.sleep() with Object.wait() but you would need to write more code do it correctly.
This is also a relevant and useful discussion.
当一个线程调用sleep方法时,该线程将被添加到一个睡眠队列中。如果计算时钟频率为100HZ,则意味着每10ms当前运行的进程就会被中断一次。保留线程的当前上下文后,它将为每个线程减少该值(-10ms)。当它为零时,线程将移至“等待CPU”队列。当时间片到达这个线程时,它就会再次运行。也因为这个并没有立即开始运行,所以实际休眠的时间比它设置的值要大。
When a thread is called the sleep method, the thread will be added into a sleep queue. If the compute clock frequency is 100HZ, that means every 10ms the current running process will be interrupted. After reserve the current context of the thread, then it will decrease the value (-10ms) for each thread. When it comes to zero, the thread will move to "waiting for CPU" queue. When time slice comes to this thread, it will be running again. Also because this which not immediately become running, so the time actually sleeps is larger than the value it set.