上两篇文章中,我们已经介绍了使用虚拟线程的例程和VirtualThread。接下来,我们继续介绍虚拟线程的调度,即VirtualThread中最重要的两个成员变量Executor scheduler和Continuation cont。
scheduler是用于虚拟线程调度的线程池,先看虚拟线程的初始化代码:
VirtualThread(Executor scheduler, String name, int characteristics, Runnable task) {
super(name, characteristics, false);
Objects.requireNonNull(task);
if (scheduler == null) {
Thread parent = Thread.currentThread();
if (parent instanceof VirtualThread vparent) {
scheduler = vparent.scheduler;
} else {
scheduler = DEFAULT_SCHEDULER;
}
}
this.scheduler = scheduler;
this.cont = new VThreadContinuation(this, task);
this.runContinuation = this::runContinuation;
}
默认调度器DEFAULT_SCHEDULER其实就是个ForkJoinPool,创建参数如下:
- parallelism,可同时执行的线程个数,取值jdk.virtualThreadScheduler.parallelism,默认为Runtime.getRuntime().availableProcessors()。
- asyncMode,异步工作模式,设置为true,表示FIFO。
- corePoolSize,池中保持的线程数,设置为0
- maximumPoolSize,池中最多保持的线程数,取值jdk.virtualThreadScheduler.maxPoolSize
- minRunnable,正在运行的最小线程数,取值jdk.virtualThreadScheduler.minRunnable
- keepAliveTime,空闲线程保活时间30秒
提交任务
虚拟线程start()和unpark()时,会调用submitRunContinuation()方法提交任务,代码如下:
try {
if (lazySubmit && scheduler instanceof ForkJoinPool pool) {
pool.lazySubmit(ForkJoinTask.adapt(runContinuation));
} else {
scheduler.execute(runContinuation);
}
}
- ForkJoinPool的lazySubmit是JDK19的新增方法,机制类似于Jetty的EatWhatYouKill,尽量让调用线程执行提交的任务。
先看看continuation的成员变量:
private static final VarHandle MOUNTED;
private volatile boolean mounted = false;
private final Runnable target;
private final ContinuationScope scope;
private Continuation parent;
private Continuation child;
初始化
public Continuation(ContinuationScope scope, Runnable target) {
this.scope = scope;
this.target = target;
}
run()
虚拟线程runContinuation会调用到Continuation的run方法,实际执行任务,代码如下:
public final void run() {
while (true) {
mount();
JLA.setExtentLocalCache(extentLocalCache);
if (done)
throw new IllegalStateException("Continuation terminated");
Thread t = currentCarrierThread();
if (parent != null) {
if (parent != JLA.getContinuation(t))
throw new IllegalStateException();
} else
this.parent = JLA.getContinuation(t);
JLA.setContinuation(t, this);
try {
boolean isVirtualThread = (scope == JLA.virtualThreadContinuationScope());
if (!isStarted()) {
enterSpecial(this, false, isVirtualThread);
} else {
assert !isEmpty();
enterSpecial(this, true, isVirtualThread);
}
} finally {
fence();
try {
assert isEmpty() == done : "empty: " + isEmpty() + " done: " + done + " cont: " + Integer.toHexString(System.identityHashCode(this));
JLA.setContinuation(currentCarrierThread(), this.parent);
if (parent != null)
parent.child = null;
postYieldCleanup();
unmount();
if (PRESERVE_EXTENT_LOCAL_CACHE) {
extentLocalCache = JLA.extentLocalCache();
} else {
extentLocalCache = null;
}
JLA.setExtentLocalCache(null);
} catch (Throwable e) { e.printStackTrace(); System.exit(1); }
}
}
}
run()方法和yield()方法配合,yield()可以中断Continuation执行,run()可以执行或者继续执行实际任务。
总共花三篇文章大概介绍了JDK19中新增的虚拟线程,从LOOM Project的纤程、阿里的wisp和wisp2、到今天JEP 425中的虚拟线程,官方提供的生产可用的用户态线程方案距离我们越来越近了。
Original: https://blog.csdn.net/a860MHz/article/details/127818347
Author: 860MHz
Title: JDK19虚拟线程初探(三)
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/659232/
转载文章受原作者版权保护。转载请注明原作者出处!