首页 > 其他分享 >JDK 19 Virtual Threads 虚拟线程

JDK 19 Virtual Threads 虚拟线程

时间:2022-11-03 20:00:20浏览次数:84  
标签:Java JDK thread 19 Thread Virtual 线程 virtual JVM

前言

JDK 19 支持了virtual thread(虚拟线程):JEP 425: Virtual Threads (Preview),虚拟线程是 Loom 项目中的一个重要特性。

Project Loom

Loom 是什么?

Loom 项目的目标是提升 Java 的并发性能。Java 自诞生就提供了线程,它是一种很方便的并发结构(先不谈线程间的通信问题 0_o),但是这种线程是使用操作系统内核线程实现的,并不能满足当前的开发需求,浪费云计算中的宝贵资源。Loom 项目引入 fiber 作为轻量级、高效的线程,fiber 由 JVM 管理,开发者可以使用和之前线程相同的操作,且 fiber 具有更好的性能、占用的内存也更少。

为什么要引入 Loom?

在二十多年前 Java 首次发布时,Java 最重要的特性之一就是能方便地访问线程,提供同步原语。Java 线程为编写并发程序提供了相对简单的抽象。但是在现在使用 Java 编写并发程序的一个问题是:并发的规模。我们希望并发服务的并发量越大越好,一个服务器能处理上万个套接字。但是由于之前的 Java 线程是使用操作系统线程实现的,在单台服务器上创建几千个套接字都很勉强了……

开发者就必须做出选择:要么直接将一个并发单元建模成线程,要么在比线程更细力度的级别上实现并发,但是需要自己编写异步代码。

Java 生态引入了异步 API,包括 JDK 的异步 NIO、异步 servlet 和异步第三方库。这些新的 API 在使用中并不优雅,而且也不容易理解,出现的原因主要是 Java 的并发单元(线程)实现得不够。仅仅是因为 Java 线程的运行时性能不够,就需要放弃线程,使用各种第三方的实现。

Java 线程使用内核线程实现固然有一些优点,比如所有的 native code 都是由内核线程支持的,所以线程中的 Java 代码能够调用 native API。但是上面提到的缺点太大了,导致难以编写高性能的代码。Erlang 和 Go 等语言都提供了轻量级线程,轻量级线程越来越流行。

Loom 项目的主要目标是添加一个通过 Java 运行时管理的叫 fiber 的轻量级线程结构,fiber 可以跟现有的中建立、操作系统的线程实现一起使用。fiber 的内存占用非常小,比内核线程轻得多,fiber 之间的任务切换开销趋近于 0。在单个 JVM 实例上就可以生成数百万个 fiber,开发者可以直接写同步阻塞的调用。同时开发者并不需要为了性能/简单性的权衡同时提供同步和异步 API。

线程并不是一个原子结构,包括 schedulercontinuation 2 个模块。Java fiber 构建在这 2 个模块之上。

Virtual threads

虚拟线程(virtual thread)就是轻量级线程,可以减少编写高吞吐高并发的应用程序的工作量。

Platform thread 是什么?

Platform thread 是操作系统线程的包装,platform Thread 在底层的 OS 线程上运行 Java 代码,数量受限于操作系统线程的数量。Platform thread 通常有比较大的线程堆栈和操作系统维护的其他资源,platform thread 也支持线程的本地变量。

可以使用 platform thread 运行所有类型的任务,就是有点浪费资源,可能会资源耗尽。

Virtual thread 是什么?

和 platform thread 一样,virtual thread 也是 java.lang.Thread 的实例。但是 virtual thread 并不与特定的操作系统线程绑定。Virtual thread 的代码仍然在操作系统的线程上运行,但是当 virtual thread 上运行的代码调用阻塞 I/O 时,Java 运行时将挂起这个 virtual thread 直到其恢复。与挂起的 virtual thread 相关联的操作系统线程可以对其他 virtual thread 执行操作。

Virtual thread 和实现方式和虚拟内存的实现方式类似。为了模拟大量内存,操作系统将一个大的虚拟地址空间映射到有限的 RAM。类似地,为了模拟大量线程,Java 运行时将大量 virtual thread 映射到少量操作系统线程上。

与 platform thread 不同,virtual thread 的调用堆栈较浅,例如只是一个 HTTP 调用或 JDBC 查询。尽管 virtual thread 支持线程局部变量,但是使用时要慎重,因为单个 JVM 可能支持数百万个 virtual thread。

Virtual thread 适合运行大部分时间被阻塞的任务(等待 I/O 操作),但并不适合 CPU 密集型操作。

Virtual thread 的好处?

Virtual thread 适合使用在高吞吐量的并发应用程序中,尤其是并发任务需要大量等待的。例如在服务器应用程序中,就需要处理很多阻塞 I/O 操作的请求。在 virtual thread 上运行的代码并不会比 platform thread 上运行的代码更快,virtual thread 的好处在于更高的吞吐量,而不是速度。

使用 virtual thread

ThreadThread.Builder API 都提供了创建 platform thread 和 virtual thread 的方法。java.util.concurrent.Executors也提供了创建使用 virtual thread 的任务的 ExecutorService

下面的代码需要使用 JDK 19,可以直接在 IDEA 中下载:

使用 Thread.Builder 创建 virtual thread

Thread thread = Thread.ofVirtual().start(() -> System.out.println("Hello"));
thread.join();

下面代码是使用 Thread.Builder 创建 2 个 virtual thread:

try {
    Thread.Builder builder =
        Thread.ofVirtual().name("worker-", 0);

    Runnable task = () -> {
        System.out.println("Thread ID: " +
            Thread.currentThread().threadId());
    };            

    // name "worker-0"
    Thread t1 = builder.start(task);   
    t1.join();
    System.out.println(t1.getName() + " terminated");

    // name "worker-1"
    Thread t2 = builder.start(task);   
    t2.join();  
    System.out.println(t2.getName() + " terminated");
    
} catch (InterruptedException e) {
    e.printStackTrace();
}

输出大概是下面这个样子:

Thread ID: 21
worker-0 terminated
Thread ID: 24
worker-1 terminated

使用 Executors.newVirtualThreadPerTaskExecutor() 创建 virtual thread

try (ExecutorService myExecutor =
    Executors.newVirtualThreadPerTaskExecutor()) {
    Future<?> future =
        myExecutor.submit(() -> System.out.println("Running thread"));
    future.get();
    System.out.println("Task completed");
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}   

调度 virtual thread

Java 运行时将 virtual thread 挂载到一个 platform thread 上,操作系统像往常一样调度 platform thread。Virtual thread 可以从对应的 platform thread 上卸载(通常发生在 virtual thread 执行阻塞 I/O 操作时)。当一个 virtual thread 被卸载后,Java 运行时调度器能挂载不同的 virtual thread。

Virtual thread 也能固定到 platform thread 上,此时在阻塞操作期间也无法卸载 virtual thread。固定的情况有:

  • virtual thread 在同步块或同步方法中(synchronized)
  • virtual thread 在运行本地方法或 外部函数

补充说明

JDK 19 使用说明

要想运行上面的代码,需要几个条件:

  • IDEA 需要升级到最新版(2022.2.3),因为最新版才包含 JDK 19 的语言特性
  • 在 Project Structure 中将 Language Level 设置为 19 (Preview)

JVM 源码分析

通过查看 JDK 8 和 JDK 19 的 Thread 源码,可以发现里面的实现已经大不一样了。对于 Thread 这块又涉及大量的操作系统底层接口,最好能直接看 JDK 源码。其实有一个完全不用下载,不用配置环境查看底层源码的方法,那就是使用 Github~~~

JDK 源码在这里:https://github.com/openjdk/jdk,里面有每个 JDK 版本的源码。

我们可以直接在里面搜索文件,响应速度还可以。

比如想查看 Thread.c 源码,直接在网页上就能查看(除了跳转功能):

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
    {"isAlive0",         "()Z",        (void *)&JVM_IsThreadAlive},
    {"setPriority0",     "(I)V",       (void *)&JVM_SetThreadPriority},
    {"yield0",           "()V",        (void *)&JVM_Yield},
    {"sleep0",           "(J)V",       (void *)&JVM_Sleep},
    {"currentCarrierThread", "()" THD, (void *)&JVM_CurrentCarrierThread},
    {"currentThread",    "()" THD,     (void *)&JVM_CurrentThread},
    {"setCurrentThread", "(" THD ")V", (void *)&JVM_SetCurrentThread},
    {"interrupt0",       "()V",        (void *)&JVM_Interrupt},
    {"holdsLock",        "(" OBJ ")Z", (void *)&JVM_HoldsLock},
    {"getThreads",       "()[" THD,    (void *)&JVM_GetAllThreads},
    {"dumpThreads",      "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
    {"getStackTrace0",   "()" OBJ,     (void *)&JVM_GetStackTrace},
    {"setNativeName",    "(" STR ")V", (void *)&JVM_SetNativeThreadName},
    {"extentLocalCache",  "()[" OBJ,    (void *)&JVM_ExtentLocalCache},
    {"setExtentLocalCache", "([" OBJ ")V",(void *)&JVM_SetExtentLocalCache},
    {"getNextThreadIdOffset", "()J",     (void *)&JVM_GetNextThreadIdOffset}
};

扩展阅读:

参考链接

我的公众号

coding 笔记、读书笔记、点滴记录,以后的文章也会同步到公众号(Coding Insight)中,希望大家关注_

标签:Java,JDK,thread,19,Thread,Virtual,线程,virtual,JVM
From: https://www.cnblogs.com/510602159-Yano/p/16855664.html

相关文章

  • 解决报错max virtual memory areas vm.max_map_count [65530] is too low, increase t
    https://blog.csdn.net/weixin_39643007/article/details/1084351391.搭建ES集群启动之后报如下的错误:   2.从报错信息vm.max_map_count看出内存太小了所以需......
  • jdk安装教程
    在点击高级系统设置之后,选择“环境变量”点击系统变量下面的新建按钮,变量名JAVA_HOME(代表你的JDK安装路径),值对应的是你的JDK的安装路径。继续在系统变量里面新建一个CLA......
  • JDK动态代理学习笔记
    JDK动态代理学习2022.10.23今天在看Java基础的时候,看到Reflect方面,资料提到各种框架离不开Reflect,同时动态代理也依赖于Reflect去随便搜了点动态代理的文章,看了看如何调......
  • 19. Remove Nth Node From End of List
    Givenalinkedlist,removethe nth nodefromtheendoflistandreturnitshead.Forexample,Givenlinkedlist: 1->2->3->4->5,and n =2.Afterremovin......
  • 实现oracle 19c pdb在cdb重启后,自动开启相应pdb
    文档课题:实现oracle19cpdb在cdb重启后,自动开启相应pdb.[oracle@dbserver~]$sql/assysdbaSQLcl:Release19.1ProductiononWedNov0214:26:192022Copyright(c)......
  • 在windows server 2016中通过dbca创建数据库时,出现DIM-00019告警
    问题描述:在windowsserver2016中通过dbca创建数据库时,出现DIM-00019告警,如下所示:数据库版本:oracle19.13搜索Mos文档,发现【DocID2652519.1】场景与问题相同.APPLIESTO:O......
  • 实战小技巧19:List转Map List的几种姿势
    今天介绍一个实用的小知识点,如何将List转为​​Map<Object,List<Object>>​​1.基本写法最开始介绍的当然是最常见、最直观的写法,当然也是任何限制的写法//比如将下面的......
  • JavaWeb期中考试-2019年版总结
    关于这次2019年期中考试的练习,我想对它进行一个总结,首先,对于完全没有接触过javaweb的我来说,只是在课上听建民老师提了一下要做这个东西,因此在一开始只是先学着怎么画html......
  • JavaWeb期中考试-2019年版(六)
    本次是JavaWeb期中考试最后一个部分,系统更新和数据显示的代码分享首先是updateq.jsp<%@pagelanguage="java"contentType="text/html;charset=UTF-8"pageEncodi......
  • 19.删除链表的倒数第N个结点
    给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。示例1:  输入:head=[1,2,3,4,5],n=2输出:[1,2,3,5]示例2:输入:head=[1],n=1输出:[]示例3:......