高并发、多线程一直是Java编程中的难点,也是面试题中的要点。Java开发者也一直在尝试使用多线程来解决应用服务器的并发问题。但是多线程并不容易,为此一个新的技术出现了,这就是虚拟线程。

传统多线程的痛点

但是编写多线程代码是非常不容易的,难以控制的执行顺序,共享变量的线程安全性,异常的可观察性等等都是多线程编程的难点。

如果每个请求在请求的持续时间内都在一个线程中处理,那么为了提高应用程序的吞吐量,线程的数量必须随着吞吐量的增长而增长。不幸的是线程是稀缺资源,创建一个线程的代价是昂贵的,即使引入了池化技术也无法降低新线程的创建成本,而且 JDK 当前的线程实现将应用程序的吞吐量限制在远低于硬件可以支持的水平。

为此很多开发人员转向了异步编程,例如CompletableFuture或者现在正热的反应式框架。但是这些技术要么摆脱不了“回调地狱”,要么缺乏可观测性。

解决这些痛点、增强Java平台的和谐,实现每个请求使用独立线程(thread-per-request style)这种风格成为必要之举。能否实现一种“成本低廉”的虚拟线程来映射到系统线程以减少对系统线程的直接操作呢?思路应该是没问题的!于是Java社区发起了关于虚拟线程的JEP 425提案。

虚拟线程

虚拟线程(virtual threads)应该非常廉价而且可以无需担心系统硬件资源被大量创建,并且不应该被池化。应该为每个应用程序任务创建一个新的虚拟线程。因此,大多数虚拟线程将是短暂的并且具有浅层调用堆栈,只执行单个任务 HTTP 客户端调用或单个 JDBC 查询。与之对应的平台线程( Platform Threads,也就是现在传统的JVM线程 )是重量级且昂贵的,因此通常必须被池化。它们往往寿命长,有很深的调用堆栈,并且在许多任务之间共享。

总而言之,虚拟线程保留了与 Java 平台的设计相协调的、可靠的独立请求线程(thread-per-request style),同时优化了硬件的利用。使用虚拟线程不需要学习新概念,甚至需要改掉现在操作多线程的习惯,使用更加容易上手的API、兼容以前的多线程设计、并且丝毫不会影响代码的拓展性。

平台线程和虚拟线程的不同

为了更好理解这一个设计,草案对这两种线程进行了比较。

现在的线程

现在每个java.lang.Thread都是一个平台线程,平台线程在底层操作系统线程上运行 Java 代码,并在代码的整个生命周期内捕获操作系统线程。平台线程数受限于 OS 线程数。

平台线程并不会因为加入虚拟线程而退出历史舞台。

未来的虚拟线程

虚拟线程是由 JDK 而不是操作系统提供的线程的轻量级实现。它们是用户模式线程的一种形式,在其他多线程语言中已经成功(比如Golang中的协程和Erlang中的进程)。 虚拟线程采用 M:N 调度,其中大量 (M) 虚拟线程被调度为在较少数量 (N) 的 OS 线程上运行。 JDK 的虚拟线程调度程序是一种ForkJoinPool工作窃取的机制,以 FIFO 模式运行。

我们可以很随意地创建10000个虚拟线程:

// 预览代码
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}

无需担心硬件资源是否扛得住,反过来如果你使用Executors.newCachedThreadPool()创建10000个平台线程,在大多数操作系统上很容易因资源不足而崩溃。

为吞吐量而设计

但是这里依然要说明一点,虚拟线程并是为了提升执行速度而设计。它并不比平台线程速度快,它们的存在是为了提供规模(更高的吞吐量),而不是速度(更低的延迟)。它们的数量可能比平台线程多得多,因此根据利特尔定律,它们可以实现更高吞吐量所需的更高并发性。

换句话说,虚拟线程可以显着提高应用程序吞吐量

  • 并发任务的数量很高(超过几千个),并且

  • 工作负载不受 CPU 限制,因为在这种情况下,拥有比处理器内核多得多的线程并不能提高吞吐量。

虚拟线程有助于提高传统服务器应用程序的吞吐量,正是因为此类应用程序包含大量并发任务,这些任务花费大量的时间等待。

增强可观测性

编写清晰的代码并不是全部。对正在运行的程序状态的清晰表示对于故障排除、维护和优化也很重要,JDK 长期以来一直提供调试、分析和监视线程的机制。 在虚拟线程中也会增强代码的可观测性,让开发人员更好地调试代码。

新的线程API

为此增加了新的线程API设计,目前放出的部分如下:

  • Thread.Builder 线程构建器。

  • ThreadFactory 能批量构建相同特性的线程工厂。

  • Thread.ofVirtual() 创建一个虚拟线程。

  • Thread.ofPlatform() 创建一个平台线程。

  • Thread.startVirtualThread(Runnable) 一种创建然后启动虚拟线程的便捷方式。

  • Thread.isVirtual() 测试线程是否是虚拟线程。

还有很多就不一一演示了,有兴趣的自行去看JEP425

总结

JEP425还有很多的细节,基于我个人理解能力的不足只能解读这么多了。协程在Java社区已经呼唤了很久了,现在终于有了实质性的动作,这是一个令人振奋的好消息。不过这个功能涉及的东西还是很多的,包括平台线程的兼容性、对ThreadLocal的一些影响、对JUC的影响。可能需要多次预览才能最终落地。胖哥可能赶不上那个时候了,不过很多年轻的同学应该能够赶上。

关注公众号:Felordcn 获取更多资讯

个人博客:https://felord.cn

Java的虚拟线程(协程)特性开启预览阶段,多线程开发的难度将大大降低的更多相关文章

  1. 进程&线程&协程

    进程  一.基本概念 进程是系统资源分配的最小单位, 程序隔离的边界系统由一个个进程(程序)组成.一般情况下,包括文本区域(text region).数据区域(data region)和堆栈(stac ...

  2. 多道技术 进程 线程 协程 GIL锁 同步异步 高并发的解决方案 生产者消费者模型

    本文基本内容 多道技术 进程 线程 协程 并发 多线程 多进程 线程池 进程池 GIL锁 互斥锁 网络IO 同步 异步等 实现高并发的几种方式 协程:单线程实现并发 一 多道技术 产生背景 所有程序串 ...

  3. python-socket和进程线程协程(代码展示)

    socket # 一.socket # TCP服务端 import socket # 导入socket tcp_sk = socket.socket() # 实例化一个服务器对象 tcp_sk.bin ...

  4. python并发编程之线程/协程

    python并发编程之线程/协程 part 4: 异步阻塞例子与生产者消费者模型 同步阻塞 调用函数必须等待结果\cpu没工作input sleep recv accept connect get 同 ...

  5. Python并发编程系列之常用概念剖析:并行 串行 并发 同步 异步 阻塞 非阻塞 进程 线程 协程

    1 引言 并发.并行.串行.同步.异步.阻塞.非阻塞.进程.线程.协程是并发编程中的常见概念,相似却也有却不尽相同,令人头痛,这一篇博文中我们来区分一下这些概念. 2 并发与并行 在解释并发与并行之前 ...

  6. C后端设计开发 - 第3章-气功-原子锁线程协程

    正文 第3章-气功-原子锁线程协程 后记 如果有错误, 欢迎指正. 有好的补充, 和疑问欢迎交流, 一块提高. 在此谢谢大家了. 童话镇 - http://music.163.com/#/m/song ...

  7. Python 进程线程协程 GIL 闭包 与高阶函数(五)

    Python 进程线程协程 GIL 闭包 与高阶函数(五) 1 GIL线程全局锁 ​ 线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的 ...

  8. python自动化开发学习 进程, 线程, 协程

    python自动化开发学习 进程, 线程, 协程   前言 在过去单核CPU也可以执行多任务,操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换任务2,任务2执行0.01秒,在切换到任务3,这 ...

  9. java压缩包上传,解压,预览(利用editor.md和Jstree实现)和下载

    java压缩包上传,解压,预览(利用editor.md和Jstree实现)和下载 实现功能:zip文件上传,后台自动解压,Jstree树目录(遍历文件),editor.md预览 采用Spring+Sp ...

随机推荐

  1. 使用flask进行mock接口

    在测试日常过程中,我们经常会遇到因为环境问题,或者是因为上下游,前后端开发进度不一,提测时间不一等情况.这时候我们可以通过mock的方式去完成一些操作.今天给大家分享一个通过flask去mock接口, ...

  2. 测试杂谈——一条SQL引发的思考

    此篇只是个人记录,相信各位大神早已轻车熟路,不喜勿喷:有错之处,欢迎指正. 有一天收到新人的咨询,是关于sql的问题. 问题1:为什么sql查询的数据与界面展示的不准确: 问题2:为什么sql查询时间 ...

  3. Numpy库基础___三

    ndarray一个强大的N维数组对象Array •ndarray的操作 索引 a = np.arange(24).reshape((2,3,4)) print(a) #[[[ 0 1 2 3] # [ ...

  4. Java基础——日期类Date

    一.概述 date代表一个特定的时间,精确到毫秒 二.构造方法 方法名 说明 public Date() 分配一个Date对象,并初始化,以便它代表被分配的时间,精确到毫秒 public Date(l ...

  5. Rsync未授权访问

    1.漏洞名称 Rsync 未授权访问漏洞 2.漏洞原理 rsync是Linux下一款数据备份工具,支持通过rsync协议.ssh协议进行远程文件传输. 其中rsync协议默认监听873端口,如果目标开 ...

  6. 关于MVC WebAPI 中加入任务调度功能的问题 (MVC WebAPI 任务调度)

    在MVC WebAPI中加入任务调度功能.即在MVC WebAPI启动时,启用任务调度程序. 但是这里有一个问题点,就是部署好IIS站点后,发现任务调度并没有启用.原因为何? 原因是部署好IIS站点后 ...

  7. JAVA视频笔记(一)

    搭建pho开发环境与框架图 韩顺平 第一章: No1  关于文件以及文件夹的管理 将生成的文本文档做成详细信息的形式,显示文件修改时间以及文件大小,便于文件查看和管理,也是对于一名IT人士高效能工作的 ...

  8. redis整理:常用命令,雪崩击穿穿透原因及方案,分布式锁实现思路,分布式锁redission(更新中)

    redis个人整理笔记 reids常见数据结构 基本类型 String: 普通key-value Hash: 类似hashMap List: 双向链表 Set: 不可重复 SortedSet: 不可重 ...

  9. web端,app端,小程序端测试差异详解

    前置解释:1.单纯从功能测试的层面上来讲的话,APP 测试.web 测试和H5测试在流程和功能测试上是没有区别的2.Web项目或pc项目都是在电脑上进行测试的.常见的PC项目架构有BS架构和CS架构的 ...

  10. 什么是 FutureTask?使用 ExecutorService 启动任务?

    在 Java 并发程序中 FutureTask 表示一个可以取消的异步运算.它有启动和取消 运算.查询运算是否完成和取回运算结果等方法.只有当运算完成的时候结果才 能取回,如果运算尚未完成 get 方 ...