JDK 21开发者新特性全解析:虚拟线程、结构化并发与记录模式

本文详细介绍了JDK 21中的六大新特性,包括虚拟线程解决传统线程模型的高成本问题,结构化并发提升多任务处理安全性,记录模式简化数据导航,以及字符串模板增强字符串操作可靠性等核心技术创新。

JDK 21开发者新特性

在Java开发者的激动人心的进展中,今年9月19日标志着JDK 21的发布。此版本包含许多有益于Java生态系统的新功能,包括虚拟线程、记录模式和序列化集合。JDK 21的预览版中还有一些有趣的功能,如字符串模板、作用域值和结构化并发。本文重点介绍了此版本中的六个新特性。

虚拟线程

Java的传统线程模型如果应用程序创建的线程数超过操作系统(OS)能处理的数量,会迅速成为昂贵的操作。此外,在线程生命周期不长的情况下,创建线程的成本很高。

虚拟线程通过将Java线程映射到管理(即挂载/卸载)线程操作到载体线程的载体线程来解决这个问题。相比之下,载体线程与OS线程协作。这是一个为开发者提供更多灵活性和控制的抽象。参见图1。

图1:JDK 21中的虚拟线程模型。

以下是虚拟线程的一个示例,并与OS/平台线程形成良好对比。程序使用ExecutorService创建10,000个任务,并等待所有任务完成。在幕后,JDK将在有限数量的载体和OS线程上运行此程序,为您提供编写并发代码的耐久性。

1
2
3
4
5
6
7
8
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    IntStream.range(0, 10_000).forEach(i -> {
        executor.submit(() -> {
            Thread.sleep(Duration.ofSeconds(1));
            return i;
        });
    });
}  // executor.close()被隐式调用,并等待

结构化并发(预览)

结构化并发与虚拟线程紧密相关,它旨在通过提供增强开发者体验的API来消除常见风险,如取消、关闭、线程泄漏等。如果一个任务拆分为并发子任务,那么它们都应返回到同一位置,即任务的代码块。

在图2中,findUser和fetchOrder都需要执行以从不同服务获取数据,然后使用该数据组合结果并将其发送回给消费者。通常,这些任务可以并发执行,但如果findUser未返回,则容易出错;fetchOrder需要等待其完成,然后最终执行Join操作。

图2:结构化并发示例。

此外,子任务的生命周期不应超过父任务本身。想象一个任务操作,如果每个操作在一个线程中执行,它将并发组合多个快速运行的I/O操作的结果。结构化并发模型通过利用虚拟线程API和StructuredTaskScope,使线程编程更接近单线程代码风格的简便性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
Response handle() throws ExecutionException, InterruptedException {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Supplier<String>  user  = scope.fork(() -> findUser());
        Supplier<Integer> order = scope.fork(() -> fetchOrder());

        scope.join()            // 加入两个子任务
             .throwIfFailed();  // ... 并传播错误

        // 这里,两个子任务都成功了,因此组合它们的结果
        return new Response(user.get(), order.get());
    }
}

序列化集合

在JDK 21中,引入了一组新的集合接口以增强使用集合的体验(图3)。例如,如果需要从集合中获取元素的逆序,取决于使用的集合,这可能会很繁琐。根据使用的集合,检索遇到顺序可能存在不一致;例如,SortedSet实现了一个,但HashSet没有,使得在不同数据集上实现这一点很麻烦。

图3:序列化集合。

为了解决这个问题,SequencedCollection接口通过添加reverse方法以及获取第一个和最后一个元素的能力来辅助遇到顺序。此外,还有SequencedMap和SequencedSet接口。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
interface SequencedCollection<E> extends Collection<E> {
    // 新方法
    SequencedCollection<E> reversed();
    // 从Deque提升的方法
    void addFirst(E);
    void addLast(E);
    E getFirst();
    E getLast();
    E removeFirst();
    E removeLast();
}

因此,现在不仅可以获取遇到顺序,还可以移除和添加第一个和最后一个元素。

记录模式

记录在Java 14中作为预览引入,这也给了我们Java枚举。record是Java中的另一种特殊类型,其目的是简化仅作为数据载体的类的开发过程。

在JDK 21中,记录模式和类型模式可以嵌套,以实现声明性和可组合的数据导航和处理形式。

1
2
3
4
5
// 创建记录:
Public record Todo(String title, boolean completed){}

// 创建对象:
Todo t = new Todo(Learn Java 21, false);

在JDK 21之前,需要解构整个记录以检索访问器。然而,现在获取值要简化得多。例如:

1
2
3
4
5
6
static void printTodo(Object obj) {
    if (obj instanceof Todo(String title, boolean completed)) {
        System.out.print(title);
        System.out.print(completed);
    }
}

记录模式的另一个优势是嵌套记录和访问它们。JEP定义本身的一个示例显示了获取Point值的能力,这些值是ColoredPoint的一部分,而ColoredPoint嵌套在Rectangle中。这比之前每次都需要解构所有记录要有用得多。

字符串模板

字符串模板是JDK 21中的预览功能。然而,它试图为字符串操作带来更多可靠性和更好的体验,以避免有时会导致不良结果(如注入)的常见陷阱。现在您可以编写模板表达式并在字符串中呈现它们。

1
2
3
4
// 从Java 21开始
String name = "Shaaf"
String greeting = "Hello \{name}";
System.out.println(greeting);

在这种情况下,第二行是表达式,调用时应呈现Hello Shaaf。此外,在有非法字符串风险的情况下,例如可能导致安全问题的SQL语句或HTML,模板规则只允许转义引号,不允许HTML文档中的非法实体。

JDK 21中的更多JEP

本文未涵盖但在另一篇文章中讨论的其他JEP包括:

  • JEP 439: 分代ZGC
  • JEP 441: switch的模式匹配
  • JEP 442: 外部函数和内存API(第三次预览)
  • JEP 443: 未命名模式和变量(预览)
  • JEP 445: 未命名类和实例主方法(预览)
  • JEP 448: 向量API(第六次孵化)
  • JEP 449: 弃用Windows 32位x86端口以移除
  • JEP 451: 准备禁止动态加载代理
  • JEP 452: 密钥封装机制API

获取Java支持

红帽客户通过订阅红帽运行时、红帽企业Linux和红帽OpenShift可获得对OpenJDK和Eclipse Temurin的支持。请联系您当地的红帽代表或红帽销售以获取更多详情。您可以根据红帽产品更新和支持生命周期获得对Java和其他运行时的支持。

资源

  • 视频:什么是Eclipse Temurin?
  • 开始使用Eclipse Temurin
  • 红帽加入Eclipse Adoptium工作组
  • Eclipse Adoptium实现其第一个Java SE版本
  • 红帽在Microsoft Windows上引入对OpenJDK的商业支持
  • OpenJDK的历史和未来
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计