JAVA19虚拟线程
虚拟线程介绍
很多语言都有类似于“虚拟线程”的技术,比如Go、C#、Erlang、Lua等,他们称之为“协程”。
不管是虚拟线程还是协程,他们都是轻量级线程,其目的都是为了提高并发能力。 本节详细介绍Java平台的“虚拟线程”的技术——“JEP 425: Virtual Threads (Preview)”。
Java平台计划引入虚拟线程,可显著减少编写、维护和观察高吞吐量并发应用程序的工作量。“JEP 425: Virtual Threads (Preview)”目是一个预览性的API。
示例代码
public static void main(String[] args) throws InterruptedException {
final List<Thread> threadList = IntStream.range(0, 10000).mapToObj(index -> Thread.ofVirtual().unstarted(() -> {
//打印线程
if (index == 0) {
System.out.println(Thread.currentThread());
}
//休眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//打印线程
if (index == 0) {
System.out.println(Thread.currentThread());
}
})).collect(Collectors.toList());
threadList.forEach(Thread::start);
for (Thread thread : threadList) {
thread.join();
}
//SpringApplication.run(DemoApplication.class, args);
}
运行结果
这里我们可以看到,java虚拟线程实际上使用了ForkJoinPool线程池。
这个时候大家就有疑问了为 什么休眠10毫秒过后,第2次打印的线程是 worker-2 ?
实际上,它可以从一个平台线程跳转到另一个,如果你在自己的电脑上操作,请确保有足够的虚拟线程, 如果只有一两个线程,是无法观测到这一点的。
这里发生了什么? 虚拟线程是如何从一个线程跳转到另一个线程的? 其实没什么魔法,核心就是一个名为Continuation的对象。
下面我带大家看看Thread.sleep的源码
可以看到,这里对 VirtualThread做了特殊的处理,如果是虚拟线程会调用 vthread.sleepNanos(nanos); 方法
在 doSleepNanos中调用了 tryYield()方法
在tryYield()里面调用了yieldContinuation()方法,yieldContinuation()方法里面调用了Continuation.yield(VTHREAD_SCOPE);
这就是奇迹发生的地方,这个Continuation对象是它的核心
Continuation对象是如何工作的
运行上面代码前,还需要在vm中加入 --add-opens java.base/jdk.internal.vm=ALL-UNNAMED,
上述代码运行结果:
大家可以看到,这个Continuation对象上调用run方法,只会执行Continuation的Runnable, 但这其实并不是我们想要的。
下面我将上面的方法做一下调整调用yield方法
ContinuationScope scope = new ContinuationScope("scope");
Continuation continuation = new Continuation(scope, () -> {
System.out.println("Running");
Continuation.yield(scope);
System.out.println("yield Running");
});
System.out.println("start");
continuation.run();
System.out.println("stop");
运行结果:
神奇的事情出现了,yield Running并没有被打印,证明 Continuation.yield(scope); 后面的方法没有被执行。
实际上Continuation.yield(scope); 会暂停它的执行。 , 接下来我将再次调用continuation.run();看看会发生什么?
ContinuationScope scope = new ContinuationScope("scope");
Continuation continuation = new Continuation(scope, () -> {
System.out.println("Running");
Continuation.yield(scope);
System.out.println("yield Running");
});
System.out.println("start");
continuation.run();
System.out.println("back");
continuation.run();
System.out.println("stop");
打印结果:
可以看到,再次运行run方法, 打印了yield Running