JDK19虚拟线程初探(三)

简介

上两篇文章中,我们已经介绍了使用虚拟线程的例程和VirtualThread。接下来,我们继续介绍虚拟线程的调度,即VirtualThread中最重要的两个成员变量Executor scheduler和Continuation cont。

scheduler

创建

scheduler是用于虚拟线程调度的线程池,先看虚拟线程的初始化代码:

    VirtualThread(Executor scheduler, String name, int characteristics, Runnable task) {
        super(name, characteristics, /*bound*/ false);
        Objects.requireNonNull(task);

        // 如果入参的scheduler为null,则做默认逻辑
        if (scheduler == null) {
            Thread parent = Thread.currentThread();
            if (parent instanceof VirtualThread vparent) {
            	// 如果当前线程是虚拟线程,则使用当前线程的scheduler
                scheduler = vparent.scheduler;
            } else {
                // 否则使用全局线程池DEFAULT_SCHEDULER
                scheduler = DEFAULT_SCHEDULER;
            }
        }

        this.scheduler = scheduler;
        // 初始化Continuation 
        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 {
        	// 调度器为ForkJoinPool的实现且lazySubmit=true时,调用ForkJoinPool的lazySubmit方法提交任务
        	// lazySubmit=true,仅在afterYield中调用
            if (lazySubmit && scheduler instanceof ForkJoinPool pool) {
                pool.lazySubmit(ForkJoinTask.adapt(runContinuation));
            } else {
            	// 否则走常规的execute提交任务
                scheduler.execute(runContinuation);
            }
        }
  • ForkJoinPool的lazySubmit是JDK19的新增方法,机制类似于Jetty的EatWhatYouKill,尽量让调用线程执行提交的任务。

Continuation

先看看continuation的成员变量:

// mounted的变量句柄,变量句柄是JDK9引入的新特性,优化了对于变量的并发访问
private static final VarHandle MOUNTED;
// 挂载还是卸载
private volatile boolean mounted = false;

// 需要执行的任务
private final Runnable target;
// 范围,虚拟线程中的ContinuationScope默认为VirtualThreads
private final ContinuationScope scope;
// 父节点,null表示native线程栈
private Continuation parent;
// 子节点,not null表示子节点中进行了yield
private Continuation child;

初始化

	// ContinuationScope,虚拟线程中的ContinuationScope默认为VirtualThreads
	// 真正需要执行的task
    public Continuation(ContinuationScope scope, Runnable target) {
        this.scope = scope;
        this.target = target;
    }

run()

虚拟线程runContinuation会调用到Continuation的run方法,实际执行任务,代码如下:

    public final void run() {
        while (true) {
        	// 通过变量句柄修改mounted状态
        	// 挂载
            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()) {
                	// 第一次执行
                	// false:继续执行为false
                    enterSpecial(this, false, isVirtualThread);
                } else {
                    assert !isEmpty();
                    // true:继续执行
                    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中的虚拟线程,官方提供的生产可用的用户态线程方案距离我们越来越近了。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值