高并发、多线程一直是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. Python IO文件管理

    文件操作 我们可以使用python来操作文件,比如读取文件内容.写入新的内容等,因为任何计算机文件的本质都是一些有不同后缀的字符组成的. python文件操作的两种模式 打开模式 while,写入模式 ...

  2. Java 8 + 10 = Java 18

    明天Java 18将正式发布, 虽然它不是长期支持 (LTS) 版本,但它却实现了九个 JEP(在Java 18列出).有哪些特性值得关注呢?今天胖哥为你提前解读.再看.点赞.转发.关注来一波吧. J ...

  3. python练习册 每天一个小程序 第0011题

    1 # -*-coding:utf-8-*- 2 3 4 def test(content): 5 flag = 0 6 with open('filtered_words.txt') as fp: ...

  4. 深入理解RPC—序列化

    深入理解RPC-序列化 xiaofang233 2020-09-18 16:38:22 1024 收藏 6分类专栏: 分布式服务版权为什么需要序列化?首先,我们得知道什么是序列化与反序列化. 我们先回 ...

  5. Spring 框架中的单例 bean 是线程安全的吗?

    不,Spring 框架中的单例 bean 不是线程安全的.

  6. 使用 Redis 有哪些好处?

    1.速度快,因为数据存在内存中,类似于 HashMap,HashMap 的优势就是查 找和操作的时间复杂度都是 O1) 2.支持丰富数据类型,支持 string,list,set,Zset,hash ...

  7. 如何给 Spring 容器提供配置元数据?

    这里有三种重要的方法给 Spring 容器提供配置元数据. XML 配置文件. 基于注解的配置. 基于 java 的配置.

  8. Linux 中进程有哪几种状态?在 ps 显示出来的信息中, 分别用什么符号表示的?

    1.不可中断状态:进程处于睡眠状态,但是此刻进程是不可中断的.不可中断, 指进程不响应异步信号. 第 441 页 共 485 页2.暂停状态/跟踪状态:向进程发送一个 SIGSTOP 信号,它就会因响 ...

  9. 学习Apache(二)

    反向代理负载均衡之APACHE 一.反向代理1.1 介绍反响代理 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将 ...

  10. 完美解决 scipy.misc.imread 报错 TypeError: Image data cannot be converted to float

    File "/home/harrison/anaconda3/lib/python3.7/site-packages/matplotlib/image.py", line 634, ...