主线程超过设定的休眠时间

The main thread exceeds the set sleep time

public static AtomicInteger num = new AtomicInteger(0);

public static void main(String[] args) throws Throwable {
    Runnable runnable = () -> {
        for (int i = 0; i < 1000000000; i++) {
            num.getAndAdd(1);
        }
    };

    Thread t1 = new Thread(runnable);
    Thread t2 = new Thread(runnable);
    t1.start();
    t2.start();

    System.out.println("before sleep");
    Thread.sleep(1000);
    System.out.println("after sleep");

    System.out.println(num);
}

我想设置主线程休眠1000ms,但实际上输出会等到两个子线程计算结束后才输出,但是当我把时间调到100ms时,主线程会不等待子线程结束。

I want to set the main thread to sleep for 1000ms

你做到了。

but in fact the output will wait until the calculation of the two sub-threads ends before outputting

你有一台电脑;这并不神奇;它的资源是有限的。您的 2 个线程是 'busy-spinning' - 时不时地工作而不是睡觉。当然,您的线程将休眠 1000 毫秒。它可能不允许 运行 因为系统正忙于做其他事情(例如你的 2 个计算线程正在忙着旋转)。

如果您想等待一个线程完成,请调用 t1.join(),它将休眠直到该线程完成。然后调用 t2.join(),瞧。

如果您想让线程暂时不做任何事情,请使用一种机制让它休眠,例如 Thread.sleepwait/notifyAlljava.util.concurrent包。比如轮询一个blockingqueue。

这是与 HotSpot 相关的重要效果 Safepoint mechanism

背景

通常 HotSpot JVM 在循环内添加一个安全点轮询,以允许在 JVM 需要执行 stop-the-world 操作时暂停线程。安全点轮询不是免费的(即它有一些性能开销),因此 JIT 编译器会尽可能地尝试消除它。其中一项优化是从 counted loops.

中删除安全点轮询

for (int i = 0; i < 1000000000; i++)是一个典型的计数循环:它有一个单调整数循环变量(计数器)和有限的迭代次数。 JDK 8 JIT 在没有安全点轮询的情况下编译这样的循环。但是,这是一个很长的循环;需要几秒钟才能完成。虽然此循环是 运行,但 JVM 将无法停止线程。

HotSpot JVM 不仅将安全点用于 GC,还用于 . In particular, it stops Java threads periodically when there are 。周期由 -XX:GuaranteedSafepointInterval 选项控制,默认为 1000 毫秒。

你的例子发生了什么

  1. 你开始了两个不可中断的长循环(内部没有安全点检查)。
  2. 主线程休眠 1 秒。
  3. 1000 毫秒 (GuaranteedSafepointInterval) 后,JVM 尝试在安全点停止 Java 线程以进行定期清理,但在计数的循环完成之前无法执行此操作。
  4. Thread.sleep 来自原生的方法returns,发现正在进行安全点操作,并挂起直到操作结束。

此时主线程正在等待循环完成 - 正是您所观察到的。

当您将休眠持续时间更改为 100 毫秒时,保证安全点发生在 Thread.sleep returns 之后,因此该方法不会被阻止。

或者,如果您保留 1000 毫秒睡眠,但增加 -XX:GuaranteedSafepointInterval=2000,主线程也不必等待。

修复

-XX:+UseCountedLoopSafepoints 选项关闭消除安全点轮询的优化。在这种情况下,Thread.sleep 将按预期休眠 1 秒。

此外,如果将 int i 更改为 long i,循环将不再被视为计数,因此您不会看到提到的安全点效果。

自 JDK10 起,HotSpot 实施了 Loop Strip Mining 优化,解决了计数循环中安全点轮询的问题,而没有太多开销。因此,您的示例在 JDK 10 及更高版本中应该开箱即用。

this issue的描述中可以找到很好的问题解释和解决方案。