Java线程的sleep()方法

4

我正在做一份Java的历年试卷,以下是其中一道题目,我感到有些困惑:

当一个线程在其run()方法中执行以下语句时,会发生什么情况?(选择所有适用项。)

sleep(500);

A. 这将停止执行,并在精确500毫秒后开始执行。 B. 这将停止执行,并且不早于500毫秒后再次开始执行。 C. 这将导致编译错误,因为您不能在 run() 方法中调用 sleep(...) 方法。 D. 这将导致编译错误,因为 sleep(...) 方法不接受任何参数。
我选择了 A 和 B,但关键答案只是 B,是否存在任何情况 A 也可能发生?能否有人为我澄清一下?非常感谢。

1
Javadoc说明:使当前正在执行的线程暂停(临时停止执行)指定的毫秒数,取决于系统计时器和调度器的精度和准确性。这取决于你的参考点。但从系统的角度来看,在重新启动之前至少会经过X时间。 - Sotirios Delimanolis
1
所有四个答案都是错误的。我曾经看到过线程休眠时间少于请求的毫秒数。这只是一个近似值,真正的休眠时间可能比请求的数字多或少。 - Dawood ibn Kareem
@DavidWallace 或许您指的是这个链接:https://blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks - Sajal Dutta
1
它是B,因为可能CPU/OS在精确时间内不会切换到线程,请参见此处 - user2511414
@DavidWallace 是正确的,但是除此之外,程序甚至无法编译,除非该类扩展了 Thread,这在问题中并没有说明。 - user207421
6个回答

2

我选择了A和B。但是正确答案只有B,是否存在任何情况下A也可能发生?请问是否有人能够澄清这一点吗?

是的,根据你的应用程序,你确实可能得到500毫秒的睡眠时间,而不会更长。

然而,之所以B是更好的答案,是因为没有任何保证任何线程何时会再次运行。你的应用程序可能有大量的CPU绑定线程。即使睡眠线程现在能够运行,它可能在很长一段时间内都无法获得任何周期。精确的睡眠时间还高度依赖于操作系统线程调度程序和时钟精度的具体情况。你的应用程序还可能与其他在同一系统上的应用程序竞争,这可能会延迟其持续执行。

例如,在我的极快的8xi7 CPU Macbook Pro上运行的以下程序显示最大睡眠时间为604毫秒:

public class MaxSleep {

    public static void main(String[] args) throws Exception {
        final AtomicLong maxSleep = new AtomicLong(0);
        ExecutorService threadPool = Executors.newCachedThreadPool();
        // fork 1000 threads
        for (int i = 0; i < 1000; i++) {
            threadPool.submit(new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < 10; i++) {
                        long total = 0;
                        // spin doing something that eats CPU
                        for (int j = 0; j < 10000000; j++) {
                            total += j;
                        }
                        // this IO is the real time sink though
                        System.out.println("total = " + total);
                        try {
                            long before = System.currentTimeMillis();
                            Thread.sleep(500);
                            long diff = System.currentTimeMillis() - before;
                            // update the max value
                            while (true) {
                                long max =  maxSleep.get(); 
                                if (diff <= max) {
                                    break;
                                }
                                if (maxSleep.compareAndSet(max, diff)) {
                                    break;
                                }
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        }
        threadPool.shutdown();
        threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
        System.out.println("max sleep ms = " + maxSleep);
    }
}

2
JVM不能保证恰好500毫秒,但它将在500毫秒或之后启动,因为它需要重新启动其“引擎”,考虑到没有其他线程阻塞任何资源可能会稍有延迟。
阅读:深入了解Hotspot VM:时钟、计时器和调度事件 编辑:正如Gray在评论中指出的那样-与其他线程的调度也是一个因素,从一个线程切换到另一个线程可能需要一些时间。

虽然这更多是关于与其他线程的调度而不是重新启动其“引擎”,但结果会增加1。在现代操作系统中,大多数情况下这只需要极少量的时间。因此答案是B。 - Gray
@Gray - 我同意。 "engine" 不是正确的表述方式,但我不知道该怎么称呼它。 - Sajal Dutta
我会说,如果线程被换出,重新换入可能需要一些时间,但是真正的问题在于与其他正在运行的线程以及正在使用CPU的其他操作系统应用程序竞争。 - Gray

1

根据Javadoc的说明:

Sleep()

使当前正在执行的线程暂停(暂时停止执行)指定的毫秒数,取决于系统计时器和调度程序的精度和准确性。线程不会失去任何监视器的所有权。

因此可能是约500毫秒

B. 它将停止执行,并在500毫秒后开始重新执行。

看起来更加突出。


1
正如您所知,线程中有两个非常相关的状态:RunningRunnable
  1. Running:表示当前正在执行的线程。它的执行正在进行中。
  2. Runnable:表示线程已准备好被处理或执行。但是正在等待被线程调度程序选取。现在,根据JVM自己的意愿/定义的算法(例如,切片算法),线程调度程序将选择其中一个可用/Runnable线程来处理它们。

因此,每当您调用sleep方法时,它仅保证运行该方法的线程的代码执行在指定的毫秒数内暂停(例如,在threadRef.sleep(300)中为300ms)。一旦定义的时间过去,它就会返回到Runnable状态(即回到可用状态以被线程调度程序选取)。

因此,不能保证在sleep方法完成后立即开始执行剩余的代码。


0

这些睡眠时间不能保证精确,因为它们受底层操作系统提供的设施的限制。选项B:不早于500更加正确。


0

你不能选择A和B,因为它们是相反的,主要区别在于:正好500毫秒后不早于500毫秒后

第一个意思就是字面意思(只有500毫秒),第二个意思是它可以睡眠501或502甚至50000000000000毫秒

下一个问题 - 为什么B是正确的,这不是一个简单的问题,你需要理解硬实时和软实时之间的区别,解释所有原因都很离题,所以简单地回答 - 由于许多技术原因,Java无法保证代码的硬实时执行,这就是为什么它声明睡眠将在不早于...之后完成

你可以阅读关于线程调度、优先级、垃圾收集、抢占式多任务处理等相关内容


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