响应式编程又变天了?看JDK21虚拟线程如何颠覆!( 二 )


4 完全响应式样式设计如何才能表现更好?达到更高标准 OKR 呢?为使该设计完全响应式 , 须以非阻塞方式获取DB、Web 服务的数据 。
作为 JDK 7 的一部分,NIO(New IO) 为非阻塞 IO 打开大门 。Java 所有基于 IO 类和方法都有非阻塞版本了 。如socket读/写、文件读/写、锁 API 等 。须使用这些类/方法的非阻塞版本或支持 NIO 的库来进行数据的获取 。
4.1 完全响应式样式设计线程图

响应式编程又变天了?看JDK21虚拟线程如何颠覆!

文章插图
图片
每个获取数据的 Fetch Data 中,发出请求的线程和获取数据的线程不同,如:
  • 从 Web 服务检索数据的 HTTP GET 请求将在一个线程上运行
  • 而最终处理检索到的数据的线程将在另一个线程上运行
这就是完全响应式,它解决了关键问题:IO 操作期间不阻塞 。在这里使用平台线程的唯一时间是在 CPU 操作期间,而不是在 IO 操作期间 。在平台线程的执行部分已看不到任何红色部分 。
这种开发风格能实现应用程序高可扩展性 。然而,解决方案过复杂 。创建响应式管道、调试它们及想象它们的执行很困难 。所以很正常,这种开发风格没有流行开来,只有顶尖的开发者才对此爱不释手 。Spring Boot 专门用于响应式风格编程的完整开发技术栈即 Spring WebFlux,它使用 Project Reactor 库提供了对DB、Web 服务等的非阻塞行为 。
5 虚拟线程还有响应式设计的其他替代方案吗?当然了!如今 Java 21 的发布,Oracle 推出备受期待的 Virtual Threads 功能 。
平台线程问题是在阻塞操作期间,实际变得无用 。平台线程基本是os线程的简易包装,毕竟os线程是昂贵的 。
而虚拟线程是 JVM 中 Thread 类的实现,它是轻量级的 。最终归结为以下几点 — 当使用虚拟线程进行代码执行时 , 它将在 CPU 操作期间使用平台线程(称为载体线程),并且在遇到 IO 操作时将载体线程释放 。
JVM如何知道何时遇到 IO 操作?虚拟线程中运行时,JVM 将自动切换到使用 IO 操作的非阻塞版本 。这种更改已在大多核心 Java 类库中为大多数 IO 操作进行了痛苦修改 。当代码遇到 IO 操作,载体线程将被释放,并且在该 IO 的数据可用时,虚拟线程将被重新安排在另一个载体线程上处理数据 。即在虚拟线程中阻塞不是问题,因为底层的载体线程被释放了 。
SE现在可选择使用虚拟线程进行用户请求 。即SE可继续使用与以前相同的命令式开发风格,同时获得使用响应式管道时获得的可扩展性优势(但没有复杂性) 。
具有虚拟线程的同步阻塞线程图这种方式在同步阻塞设计中的情况(注意,阻塞不是一个问题) 。
响应式编程又变天了?看JDK21虚拟线程如何颠覆!

文章插图
图片
用户请求线程是虚拟线程(蓝色垂直箭头) 。线程上的红色不再是问题,因为阻塞操作期间,底层的载体线程将被释放 , 从而实现与使用响应式框架相同的可扩展性优势 。
6 虚拟线程和异步阻塞设计6.1 异步阻塞设计中的虚拟线程阻塞在此也不再是问题 。前面提到可用 Java Futures 实现,我们确实有这样做的选择 。但Java 21引入 StructuredTaskScope 和 Subtask ,以处理结构化异步行为 。
响应式编程又变天了?看JDK21虚拟线程如何颠覆!

文章插图
图片
虚拟线程 和 StructuredTaskScope 的组合将非常强大 。虚拟线程使阻塞不再是一个问题,而 StructuredTaskScope 将为我们提供更高级别的类,以直观的方式处理异步编程 。很难看到为什么还需要 Completable Futures 。
虚拟线程 V.S 响应式框架
  • 可继续使用命令式风格开发
  • 无需创建复杂的响应式管道
  • 无需在代码中直接使用非阻塞 IO
  • 更易编码、调试和理解
7 总结随着 Java 21 中 虚拟线程 引入,虚拟线程在阻塞状态下不再是问题 。开发人员:
  • 无需创建复杂的响应式风格管道
  • 且无需在代码中直接使用非阻塞 IO
即可创建高度可扩展的应用程序 。替代方案是使用 Java 21 中引入的 虚拟线程 与 Java Futures 或 Structured Concurrency(Java 21 中的预览功能) 类的组合 。


推荐阅读