Java虚拟线程(三)实现原理
https://mp.weixin.qq.com/s/vdLXhZdWyxc6K-D3Aj03LA
虚拟线程原理
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;
import java.util.concurrent.TimeUnit;
public class ContinuteTest {
public static void main(String[] args) throws InterruptedException {
//这个ContinuationScope 类似一个锚点,跟一个Continuation绑定,后续Continuation如果暂停的时候需要指定这个锚点。
ContinuationScope scope = new ContinuationScope("scope");
//Continuation是封装了的Runnable
Continuation continuation = new Continuation(scope, () -> {
System.out.println("before yield开始");
Continuation.yield(scope); // 这里会从载体线程卸载
System.out.println("after yield 结束");
});
System.out.println("1 run");
continuation.run(); // 第一次执行Continuation.run。 载体线程main,除了执行自己的逻辑之外,也通过run执行了continuation的逻辑,执行到yield为止
System.out.println("2 run");
System.out.println("Done");
continuation.run(); // 第二次执行Continuation.run 。 从continuation 之前yield的地方继续执行
//TimeUnit.SECONDS.sleep(1);
}
}
注意:本例要加jvm启动参数--add-exports java.base/jdk.internal.vm=ALL-UNNAMED,不然是没法访问jdk.internal.vm下的类的。
运行结果:
1 run
before yield开始
2 run
Done
after yield 结束
上面的例子里只有一个真正的线程,main线程,也就是载体线程干活。。它一直在做System.out和执行虚拟线程逻辑之间切换,是串行执行。它也可以叫做平台线程,跟os线程是1:1
虚拟线程是通过Continuation来模拟的 (实际上Java虚拟线程底层也就是这东西);
Java虚拟线程的本质就是用Continuation封装Runnable, 如上所示,Continuation 执行过程中可以使用yield使之从载体线程卸载,再次执行run()将会继续执行Continuation的runnable逻辑。
卸载这个过程是native代码,JVM做的事情,对开发者透明。
- 这个过程中会将载体线程此时的线程栈中涉及虚拟线程执行的栈帧(从run到yield之间)复制到堆中(没错,虚拟线程的“线程栈”是在堆里的),然后清空,载体线程就继续被ForkJoinPool (注:我们这个例子只是主线程,不涉及ForkJoinPool)调度去干别的事情。
- 第二次continuation.run()恢复执行,那么JVM做相反的事情,从堆中恢复之前的快照到载体线程栈,继续执行。
实践中虚拟线程的创建方法
当然了,上面的例子是为了演示,实际上一般Java是不可以直接在一个线程里启虚拟线程的,就像上面的main一样。在实际使用 java.lang.VirtualThread 时,虚拟线程会被提交给一个全局的 ForkJoinPool 调度器,由该池中的工作线程作为载体来执行。
实际上是统一有一个全局的ForkJoinPool线程池来统一做载体线程的,通过 https://www.cnblogs.com/lyhero11/p/20448435 中介绍的方法,都是常用创建虚拟线程的方法。
浙公网安备 33010602011771号