前言
看博客的时候看到一个有趣的文章 https://juejin.cn/post/7139741080597037063,讲的是在一个for循环中加入了Thread.sleep(0),看似无用的代码其实有很多学问。 在此做个简化和总结。
- 为什么要加Thread.sleep(0)
涉及到了安全点(safepoint),为了能够让jvm发现并进行垃圾回收,我们用原作者的一个例子说明一下
public class MainTest {
public static AtomicInteger num = new AtomicInteger(0);
public static void main(String[] args) throws InterruptedException {
Runnable runnable=()->{
for (int i = 0; i < 1000000000; i++) {
num.getAndAdd(1);
}
System.out.println(Thread.currentThread().getName()+"执行结束!");
};
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println("num = " + num);
}
}
按照代码来看,主线程休眠 1000ms 后就会输出结果,但是实际情况却是主线程一直在等待 t1,t2 执行结束才继续执行。
这就是一种可数循环。
这个程序发生了什么事情呢?
1.启动了两个长的、不间断的循环(内部没有安全点检查)。
2.主线程进入睡眠状态 1 秒钟。
3.在1000 ms之后,JVM尝试在Safepoint停止,以便Java线程进行定期清理,但是直到可数循环完成后才能执行此操作。
4.主线程的 Thread.sleep 方法从 native 返回,发现安全点操作正在进行中,于是把自己挂起,直到操作结束。
当我们把for循环中的int改成long后就正常了
我们使用Thread.sleep(0)测试一下
Runnable runnable=()->{
for (int i = 0; i < 1000000000; i++) {
num.getAndAdd(1);
if(i % 1000 ==0) {
try {
Thread.sleep(0);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
System.out.println(Thread.currentThread().getName()+"执行结束!");
};
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
t1.start();
t2.start();
Thread.sleep(1000);
System.out.println("num = " + num);
执行结果:
这样,即使 for 循环的对象是 int 类型,也可以按照预期执行。因为我们相当于在循环体中插入了 Safepoint。
- 预分配文件
关于什么事安全点(safepoint)和什么是可数循环请看前言中原作者的文章,写的非常棒。
预分配文件也可以在原文章中看到,这里只是做一个简单的总结,谢谢