glo*_*one 1 java multithreading jvm
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);
}
Run Code Online (Sandbox Code Playgroud)
我想设置主线程休眠1000ms,但实际上输出会等到两个子线程计算结束才输出,但是当我把时间调整为100ms时,主线程不会等到结束的子线程。
这是与 HotSpot Safepoint 机制相关的重要效果。
通常,HotSpot JVM 在循环内添加安全点轮询,以便在 JVM 需要执行 stop-the-world 操作时暂停线程。安全点轮询不是免费的(即它有一些性能开销),因此 JIT 编译器会尝试在可能的情况下消除它。其中一种优化是从计数循环中删除安全点轮询。
for (int i = 0; i < 1000000000; i++)是一个典型的计数循环:它具有单调整数循环变量(计数器)和有限的迭代次数。JDK 8 JIT 在没有安全点轮询的情况下编译此类循环。然而,这是一个非常长的循环;需要几秒钟才能完成。当这个循环运行时,JVM 将无法停止线程。
HotSpot JVM 不仅将安全点用于 GC,还用于许多其他操作。特别是,当有清理任务需要执行时,它会定期停止 Java 线程。周期由-XX:GuaranteedSafepointInterval选项控制,默认为 1000 ms。
Thread.sleep方法从本机返回,发现安全点操作正在进行中,并挂起直到操作结束。此时,主线程正在等待循环完成 - 正如您所观察到的那样。
当您将睡眠持续时间更改为 100 毫秒时,保证的安全点会在Thread.sleep返回后发生,因此该方法不会被阻塞。
或者,如果您保留 1000 毫秒睡眠时间,但增加-XX:GuaranteedSafepointInterval=2000,主线程也不必等待。
-XX:+UseCountedLoopSafepoints选项关闭消除安全点轮询的优化。在这种情况下,Thread.sleep将按预期休眠 1 秒。
另外,如果更改int i为long i,则循环将不再被视为计数,因此您不会看到提到的安全点效果。
从 JDK 10 开始,HotSpot 实现了 Loop Strip Mining 优化,无需太多开销即可解决计数循环中的安全点轮询问题。因此,您的示例应该可以在 JDK 10 及更高版本中正常运行。
问题的详细解释和解决方案可以在这个问题的描述中找到。
| 归档时间: |
|
| 查看次数: |
708 次 |
| 最近记录: |