百日筑基第五十七天-虚拟线程

news2024/11/16 2:23:42

百日筑基第五十七天-虚拟线程

前提

JDK192022-09-20发布GA版本,该版本提供了虚拟线程的预览功能。下载JDK19之后翻看了一下有关虚拟线程的一些源码,跟早些时候的Loom项目构建版本基本并没有很大出入,也跟第三方JDK如鹅厂的Kona虚拟线程实现方式基本一致,这里分析一下虚拟线程设计与源码实现。

Platform Thread与Virtual Thread

因为引入了虚拟线程,原来JDK存在java.lang.Thread类,俗称线程,为了更好地区分虚拟线程和原有的线程类,引入了一个全新类java.lang.VirtualThreadThread类的一个子类型),直译过来就是"虚拟线程"。

Thread在此基础上做了不少兼容性工作。此外,还应用了建造者模式引入了线程建造器,提供了静态工厂方法Thread.ofPlatform()Thread.ofVirtual()分别用于实例化Thread(工厂)建造器和VirtualThread(工厂)建造器,顾名思义,两种建造器分别用于创建Thread或者VirtualThread,例如:

// demo-1 build platform thread
Thread platformThread = Thread.ofPlatform().daemon().name("worker").unstarted(runnable);

// demo-2 create platform thread factory
ThreadFactory platformThreadFactory = Thread.ofPlatform().daemon().name("worker-", 0).factory();

// demo-3 build virtual thread
Thread virtualThread = Thread.ofVirtual().name("virtual-worker").unstarted(runnable);

// demo-4 create virtual thread factory
ThreadFactory virtualThreadFactory = Thread.ofVirtual().name("virtual-worker-", 0).factory();

更新的JDK文档中也把原来的Thread称为Platform Thread,可以更明晰地与Virtual Thread区分开来。这里Platform Thread直译为"平台线程",其实就是"虚拟线程"出现之前的老生常谈的"线程"。

那么平台线程与虚拟线程的联系和区别是什么?JDK中的每个java.lang.Thread实例也就是每个平台线程实例都在底层操作系统线程上运行Java代码,并且平台线程在运行代码的整个生命周期内捕获系统线程。可以得出一个结论,平台线程与底层系统线程是一一对应的,平台线程实例本质是由系统内核的线程调度程序进行调度,并且平台线程的总数量受限于系统线程的总数量。

在这里插入图片描述

总的来说,平台线程有下面的一些特点或者说限制:

  • 资源有限导致系统线程总量有限,进而导致与系统线程一一对应的平台线程有限
  • 平台线程的调度依赖于系统的线程调度程序,当平台线程创建过多,会消耗大量资源用于处理线程上下文切换
  • 每个平台线程都会开辟一块私有的栈空间,大量平台线程会占据大量内存

这些限制导致开发者不能极大量地创建平台线程,为了满足性能需要,需要引入池化技术、添加任务队列构建消费者-生产者模式等方案去让平台线程适配多变的现实场景。显然,开发者们迫切需要一种轻量级线程实现,刚好可以弥补上面提到的平台线程的限制,这种轻量级线程可以满足:

  • 可以大量创建,例如十万级别、百万级别,而不会占据大量内存
  • JVM进行调度和状态切换,并且与系统线程"松绑"
  • 用法与原来平台线程差不多,或者说尽量兼容平台线程现存的API

虚拟线程就是为了解决这个问题,看起来它的运行示意图如下:

在这里插入图片描述

(当然,平台线程不是简单地与虚拟线程进行1:N的绑定)

虚拟线程实现原理

虚拟线程是一种轻量级(用户模式)线程,这种线程是由Java虚拟机调度,而不是操作系统。虚拟线程占用空间小,任务切换开销几乎可以忽略不计,因此可以极大量地创建和使用。总体来看,虚拟线程实现如下:

virtual thread = continuation + scheduler

虚拟线程会把任务(一般是java.lang.Runnable)包装到一个Continuation实例中:

  • 当任务需要阻塞挂起的时候,会调用Continuationyield操作进行阻塞
  • 当任务需要解除阻塞继续执行的时候,Continuation会被继续执行

Scheduler也就是执行器,会把任务提交到一个载体线程池中执行:

  • 执行器是java.util.concurrent.Executor的子类
  • 虚拟线程框架提供了一个默认的ForkJoinPool用于执行虚拟线程任务

下文会把carrier thread称为"载体线程",指的是负责执行虚拟线程中任务的平台线程,或者说运行虚拟线程的平台线程称为它的载体线程

操作系统调度系统线程,而Java平台线程与系统线程一一映射,所以平台线程被操作系统调度,但是虚拟线程是由JVM调度。JVM把虚拟线程分配给平台线程的操作称为mount(挂载),反过来取消分配平台线程的操作称为unmount(卸载):

  • mount操作:虚拟线程挂载到平台线程,虚拟线程中包装的Continuation栈数据帧或者引用栈数据会被拷贝到平台线程的线程栈,这是一个从堆复制到栈的过程
  • unmount操作:虚拟线程从平台线程卸载,大多数虚拟线程中包装的Continuation栈数据帧会留在堆内存中

这个mount -> run -> unmount过程用伪代码表示如下:

mount();
try {
    Continuation.run();
} finally {
    unmount();
}

Java代码的角度来看,虚拟线程和它的载体线程暂时共享一个OS线程实例这个事实是不可见,因为虚拟线程的堆栈跟踪和线程本地变量与平台线程是完全隔离的。JDK中专门是用了一个FIFO模式的ForkJoinPool作为虚拟线程的调度程序,从这个调度程序看虚拟线程任务的执行流程大致如下:

  • 调度器(线程池)中的平台线程等待处理任务

在这里插入图片描述

  • 一个虚拟线程被分配平台线程,该平台线程作为运载线程执行虚拟线程中的任务

在这里插入图片描述

  • 虚拟线程运行其Continuation,从而执行基于Runnable包装的用户任务

在这里插入图片描述

  • 虚拟线程任务执行完成,标记Continuation终结,标记虚拟线程为终结状态,清空一些上下文变量,运载线程"返还"到调度器(线程池)中作为平台线程等待处理下一个任务

在这里插入图片描述

上面是描述一般的虚拟线程任务执行情况,在执行任务时候首次调用Continuation#run()获取锁(ReentrantLock)的时候会触发Continuationyield操作让出控制权,等待虚拟线程重新分配运载线程并且执行,见下面的代码:

public class VirtualThreadLock {

    public static void main(String[] args) throws Exception {
        ReentrantLock lock = new ReentrantLock();
        Thread.startVirtualThread(() -> {
            lock.lock();     // <------ 这里确保锁已经被另一个虚拟线程持有
        });
        Thread.sleep(1000);
        Thread.startVirtualThread(() -> {
            System.out.println("first");
            lock.lock();
            try {
                System.out.println("second");
            } finally {
                lock.unlock();
            }
            System.out.println("third");
        });
        Thread.sleep(Long.MAX_VALUE);
    }
}

  • 虚拟线程中任务执行时候首次调用Continuation#run()执行了部分任务代码,然后尝试获取锁,会导致Continuationyield操作让出控制权(任务切换),也就是unmount,运载线程栈数据会移动到Continuation栈的数据帧中,保存在堆内存,虚拟线程任务完成(但是虚拟线程没有终结,同时其Continuation也没有终结和释放),运载线程被释放到执行器中等待新的任务;如果Continuationyield操作失败,则会对运载线程进行park调用,阻塞在运载线程上

在这里插入图片描述

  • 当锁持有者释放锁之后,会唤醒虚拟线程获取锁(成功后),虚拟线程会重新进行mount,让虚拟线程任务再次执行,有可能是分配到另一个运载线程中执行,Continuation栈会的数据帧会被恢复到运载线程栈中,然后再次调用Continuation#run()恢复任务执行:

在这里插入图片描述

  • 最终虚拟线程任务执行完成,标记Continuation终结,标记虚拟线程为终结状态,清空一些上下文变量,运载线程"返还"到调度器(线程池)中作为平台线程等待处理下一个任务

Continuation组件十分重要,它既是用户真实任务的包装器,也是任务切换虚拟线程与平台线程之间数据转移的一个句柄,它提供的yield操作可以实现任务上下文的中断和恢复。由于Continuation被封闭在java.base/jdk.internal.vm下,可以通过增加编译参数--add-exports java.base/jdk.internal.vm=ALL-UNNAMED暴露对应的功能,从而编写实验性案例,IDEA中可以按下图进行编译参数添加:

在这里插入图片描述

然后编写和运行下面的例子:

import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;

public class ContinuationDemo {

    public static void main(String[] args) {
        ContinuationScope scope = new ContinuationScope("scope");
        Continuation continuation = new Continuation(scope, () -> {
            System.out.println("Running before yield");
            Continuation.yield(scope);
            System.out.println("Running after yield");
        });
        System.out.println("First run");
        // 第一次执行Continuation.run
        continuation.run();
        System.out.println("Second run");
        // 第二次执行Continuation.run
        continuation.run();
        System.out.println("Done");
    }
}

// 运行代码,神奇的结果出现了
First run
Running before yield
Second run
Running after yield
Done

这里可以看出Continuation的奇妙之处,Continuation实例进行yield调用后,再次调用其run方法就可以从yield的调用之处往下执行,从而实现了程序的中断和恢复。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2057502.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode JAVA刷刷站(58)最后一个单词的长度

一、题目概述 二、思路方向 要解决这个问题&#xff0c;你可以通过遍历字符串 s 并从后往前计数的方式来实现。但更简洁且易于理解的方法是&#xff0c;首先去除字符串尾部的空格&#xff08;如果有的话&#xff09;&#xff0c;然后找到最后一个单词的起始位置&#xff0c;并计…

I2C学习:传输速率

一&#xff0e;内容简介 I2C总线根据传输速度不同&#xff0c;可以划分为5种速度模式&#xff0c;见下列表格。 速度模式 最高速率 备注 标准模式Sm 100Kbps 双向传输 向下兼容 快速模式Fm 400Kbps 快速模式增强Fm 1Mbps 高速模式HSm 3.4Mbps 超快速模式UFm 5Mbp…

解决在移动端css使用100vh底部被遮盖的问题

原文引用&#xff1a;https://blog.csdn.net/hw_happy/article/details/132421653 移动端下&#xff0c;若使用100vh单位&#xff0c;那么高度不会是浏览器可视区域的高度&#xff0c;而是会高于可视区域&#xff0c;所以居底部的元素会被遮盖住&#xff1a; 如果是chrome浏览…

autocommit自动提交事务及commit、rollback用法

MySQL默认开启事务自动提交&#xff0c;每条SQL语句都会被当做一个单独的事务自动执行。 一、查看autocommit自动提交事物状态 SHOW VARIABLES LIKE ‘autocommit’; 1、开启状态&#xff1a;ON autocommit的值为ON&#xff0c;表示系统开启自动提交模式 2、关闭状态&#…

【VIsion Master】机器视觉软件二次开发(C#版本)学习笔记

0.前言 最近接手新项目&#xff0c;用海康威视旗下的HIK ROBOT Vision Master机器视觉软件做二次开发相关的项目&#xff0c;写一篇博客记录一下学习过程。 参考视频&#xff1a;https://www.bilibili.com/video/BV1tq4y1j7RP?p1 其他参考资料&#xff1a;软件自带的开发文档…

JavaScript语法基础之流程结构(顺序、选择、循环结构)

目录 1. 流程控制 1.1. 流程控制简介 1.1.1. 顺序结构 1.1.2. 选择结构 1.1.3. 循环结构 1.2. 选择结构&#xff1a;if 1.2.1. 单向选择&#xff1a;if… 1.2.2. 双向选择&#xff1a;if…else… 1.2.3. 多向选择&#xff1a;if…else_if…else… 1.3. 选择结构&#…

一口气学完Python编程语言的基础内容

文章目录 第1章 Python简介1.1 Python简介1.2 Python历史1.3 Python特点1.4 Python的应用场景1.5 Python的版本 第2章 Python环境搭建2.1 Python软件安装2.2 编辑器安装 第3章 Python基础语法3.1 第一个Python程序3.1.1 使用 Python 命令行3.1.2 使用 IPython3.1.3 使用 PyChar…

从新手到专家,2024年免费视频编辑软件成长之路

随着人们开始用视频来表达自己的看法、生活&#xff0c;促进来数字媒体和社交媒体的发展。用来处理视频的工具也越来越多&#xff0c;我们要怎么从一众的视频剪辑工具里找到属于自己的那一款免费视频剪辑软件呢&#xff1f;这次我们就来浅浅分析一下。 1.福昕视频剪辑 连接直…

Linux timedatectl 命令

timedatectl 是 Linux 系统中用于查询和更改系统日期、时间和时区的工具&#xff0c;它特别适用于那些使用 systemd 作为系统和服务管理器的系统。语法格式为“timedatectl [参数]”。 发现电脑时间跟实际时间不符&#xff1f;如果只差几分钟的话&#xff0c;我们可以直接调整。…

接受三个数字参数,返回最大

def mostNum(*nums): #nums为元组&#xff08;不支持修改&#xff09;&#xff0c;转化为列表liNumslist(nums)for i in range(0,len(liNums)-1): #冒泡法if liNums[i]>liNums[i1]:cliNums[i]liNums[i]liNums[i1]liNums[i1]creturn liNums.pop() #列表最后一个最大的数被返…

花钱买不到系列—linux虚拟地址空间

不知道大家有没有听说过一个东西&#xff0c;叫C/C地址空间。 给大家画一个图这个图&#xff0c;作为C/C程序员应该比较熟悉&#xff0c;有人一个会有人把这个图叫做C/C地址空间&#xff0c;我觉得大家应该比较陌生&#xff0c;我也是刚刚学完&#xff0c;大家如果感兴趣的&…

计算机毕业设计 毕业季旅游一站式定制服务平台 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

圈子论坛小程序搭建教程,系统快速部署上线指南,支持文章、源码、链接等上传

圈子论坛小程序是一种基于移动端的社交平台&#xff0c;旨在为用户提供交流分享、互动沟通的空间。以下是关于圈子论坛小程序的详细解析&#xff1a; 一、圈子论坛小程序的定义与功能 定义&#xff1a;圈子论坛小程序是一个集社交、分享、交流于一体的移动应用&#xff0c;用户…

Unity视频流监控接入,点击播放只播放一帧画面

Universal Media Player 检查监控平台编码/解码配置&#xff08;H265/H264&#xff09;

安卓sdk manager下载安装

安卓sdk下载安装 android SDK manager下载 环境变量配置 ANDROID_HOME&#xff1a;D:\Android %ANDROID_HOME%\tools %ANDROID_HOME%\platform-tools %ANDROID_HOME%\build-tools\29.0.3Android SDK Platform-tools公用开发工具包&#xff0c;需要下载 Android SDK Tools基础…

llvm windows编译成功

一、所需工具 Visual Studio 推荐版本&#xff1a;Visual Studio 2022。其他版本亦可支持。 CMake 下载地址 Ninja 下载地址 LLVM 版本参考&#xff1a;llvm-project-llvmorg-18.1.8下载地址 二、配置与编译步骤 以管理员身份打开命令行终端&#xff0c;输入以下命令来设置…

Linux多进程

进程的概述 进程是计算机科学中的一个基本概念&#xff0c;它指的是在操作系统中正在执行的程序的实例 在Linux操作系统中&#xff0c;进程是程序执行的实体&#xff0c;是资源分配的基本单位 在在Ubuntu中&#xff0c;通过使用ps命令可以查看当前的进程列表 ps aux 进程与…

WSL2安装与使用

使用WSL2的前提条件&#xff1a; 1.开启CPU的虚拟化 打开任务管理器 ->性能->查看CPU虚拟化 2.开启Windows功能 任务栏输入“功能”&#xff0c;勾选下面选项&#xff0c;然后按照提示重新启动电脑。 3.搜索栏输入cmd&#xff0c;右键以管理员身份运行&#xff0c;输入…

对商品评论进行文本分析(NLP)的实战项目

文本分析技术是指使用计算机程序或算法处理、分析和理解文本数据的一系列方法。这种技术在自然语言处理&#xff08;NLP&#xff09;领域中非常重要&#xff0c;它可以应用于多种场景&#xff0c;包括但不限于情感分析、主题识别、信息提取、文本分类等。以下是一些常见的文本分…

SpringBoot+Vue3整合minio,实现分布式文件存储

文章目录 几种常用的文件存储安装和使用minioSpringBoot整合minio 基本所有的软件项目都会需要文件存储功能&#xff0c;图片、视频存储。 几种常用的文件存储 经常用的几种方案&#xff0c;直接存在本地文件夹&#xff0c;开发一个简单的系统当然没有问题。随机系统所需的资源…