Thread.sleep() 实现

Posted

技术标签:

【中文标题】Thread.sleep() 实现【英文标题】:Thread.sleep() implementation 【发布时间】:2012-02-22 18:03:26 【问题描述】:

今天我参加了一次面试,我问了候选人关于Thread.sleep()Object.wait() 之间区别的非常普通和基本的问题。我希望他回答like this之类的问题,但他说这些方法基本上是一样的,很可能Thread.sleep在里面使用Object.wait(),但sleep本身不需要外部锁。这不是一个完全正确的答案,因为在 JDK 1.6 中,此方法具有以下签名。

public static native void sleep(long millis) throws InterruptedException;

但我的第二个想法是这并不荒谬。可以使用定时等待来达到相同的效果。看看下面的代码sn -p:

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)。根据this article:进行调度管理是有意义的

这具有清除当前线程的量程并将其置于队列末尾以获得其优先级的特殊效果。换句话说,所有具有相同优先级(以及更高优先级)的可运行线程将有机会在产生的线程下一次给定 CPU 时间之前运行。

所以synchronized 块会产生不必要的开销。

您知道在Thread.sleep() 实现中不使用定时等待的其他原因吗?

【问题讨论】:

wait(millis) 可以虚假(早)醒来,而 sleep 除非被中断,否则不会早醒。 在调用 sleep() 期间是否检查了中断标志以便抛出异常并返回?或者不知何故,这个调用也转到了调度程序,然后调度程序知道结束线程的睡眠? 我会更关心他们是否知道如何正确处理 InterruptedException。 【参考方案1】:

您知道在Thread.sleep() 实现中不使用定时等待的任何其他原因吗?

因为原生线程库提供了完美的睡眠功能:http://www.gnu.org/software/libc/manual/html_node/Sleeping.html

要了解为什么原生线程很重要,请从http://java.sun.com/docs/hotspot/threads/threads.html开始

1.1 版基于绿色线程,此处不再赘述。绿色线程是虚拟机中的模拟线程,在 1.2 及更高版本中使用本机操作系统线程模型之前使用。绿色线程可能曾经在 Linux 上具有优势(因为您不必为每个本机线程生成一个进程),但 VM 技术自 1.1 版以来已显着进步,过去绿色线程的任何好处都被抹去了多年来性能不断提高。

【讨论】:

【参考方案2】:

人们可以很容易地说奥卡姆剃刀切入了另一种方式。 JVM 底层 JDK 的正常/预期实现被假定为大多数时间将 java“线程”绑定到本机线程,并且将线程置于睡眠状态是底层平台的基本功能。如果线程代码无论如何都是本机的,为什么要在 java 中重新实现它?最简单的解决方案是使用已有的功能。

其他一些注意事项: 在现代 JVM 中,无争议的同步可以忽略不计,但并非总是如此。过去,获取该对象监视器是一项相当“昂贵”的操作。

如果您在 java 代码中实现线程休眠,并且您实现它的方式也不会绑定到本机线程等待,则操作系统必须继续调度该线程以运行检查是否该唤醒的代码向上。正如 cmets 中所讨论的那样,对于您在现代 JVM 上的示例来说,这显然不是真的,但很难说 1) 在第一次以这种方式指定 Thread 类时可能已经存在和预期的内容。 和 2)如果该断言适用于每个平台,那么人们可能曾经想在其上实现 JVM。

【讨论】:

@ruakh,我猜当本机线程在 pthread 之上实现时,pthread_cond_*waitObject.wait 的基础。 人们会期望现代 JVM 和现代平台肯定会这样做。然而,OP 的问题是“10 多年前一些 Sun 工程师可能做出这个决定的原因是什么?”,而不是关于现在事情的实际运作方式。 :) @ruakh,当我阅读 Affe 的答案时,我没有那种印象。 不,这意味着 java 对象可以封装提供监视器 wait() 功能的 OS 同步原语。在 object.wait() 上阻塞的线程不会被操作系统分配任何 CPU,就像睡眠线程一样。 @ruakh 现在肯定不是真的。答案是对一个投机性问题的投机性回应,即为什么一个决定可能是很久以前做出的。我会想办法改写它,以免误导。【参考方案3】:

Thread.sleep() 不会被spurious wakeups 提前唤醒。如果使用 Object.wait(),要正确执行(即确保您等待足够的时间),您需要一个循环来查询已用时间(例如 System.currentTimeMillis()),以确保您等待足够的时间。

从技术上讲,您可以使用 Object.wait() 实现与 Thread.sleep() 相同的功能,但您需要编写更多代码才能正确完成。

This 也是一个相关且有用的讨论。

【讨论】:

这两个函数有不同的用途。您可以使用一个来模拟另一个,但这会很棘手,而且无疑不如已经提供的实现。 是的,这是有道理的。我忘记了虚假唤醒,看起来 Thread.sleep() 不受这种现象的影响。【参考方案4】:

当一个线程被调用 sleep 方法时,该线程将被加入到一个睡眠队列中。如果计算时钟频率为 100HZ,这意味着每 10ms 当前正在运行的进程将被中断一次。在保留线程的当前上下文后,它将为每个线程减少值(-10ms)。当它归零时,线程将移动到“等待 CPU”队列。当时间片来到这个线程时,它将再次运行。也因为 this 没有立即运行,所以实际休眠的时间比它设置的值大。

【讨论】:

以上是关于Thread.sleep() 实现的主要内容,如果未能解决你的问题,请参考以下文章

Thread.sleep() 到摇摆定时器转换

隐藏 Thread.sleep

替代 Thread.Sleep

java 实现等待时间

timeunit的sleep和thread的sleep有啥区别

Thread.Sleep()导致界面卡死问题