Java 21 新特性终极指南:从虚拟线程到模式匹配的全面进化

_

Java 21 新特性详解:下一代LTS的全面进化

2023年9月,Java 21 正式发布,作为继 Java 17 之后最新的**长期支持(LTS)版本,它标志着 Java 平台又一次里程碑式的飞跃。Java 21 不仅带来了备受瞩目的虚拟线程(Virtual Threads)**正式投产,还引入了一系列语言增强、API 改进和性能优化,让 Java 开发者能够以更简洁、高效的方式构建高并发应用。本文将深入剖析 Java 21 的核心新特性,并通过代码示例展示如何在实际项目中运用它们。


1. 虚拟线程(Virtual Threads)——JDK 21 的明星特性

虚拟线程是 Project Loom 的最终成果,旨在简化高并发应用的开发。传统的平台线程(Platform Thread)直接映射到操作系统内核线程,数量有限且创建成本高。而虚拟线程是 JDK 管理的轻量级线程,数量可达数百万,极大提升了服务器的并发处理能力。

1.1 创建虚拟线程的几种方式

// 方式1:使用 Thread.startVirtualThread()
Thread vThread = Thread.startVirtualThread(() -> {
    System.out.println("Hello from virtual thread!");
});

// 方式2:使用 Thread.ofVirtual() 构建器
Thread vThread2 = Thread.ofVirtual()
        .name("myVirtualThread")
        .unstarted(() -> System.out.println("Virtual thread started"));
vThread2.start();

// 方式3:使用 Executors.newVirtualThreadPerTaskExecutor()
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    executor.submit(() -> System.out.println("Task in virtual thread"));
}

1.2 虚拟线程与平台线程的对比

传统基于线程池的模型,每个任务占用一个平台线程,而虚拟线程允许每个任务独占一个虚拟线程,代码编写如同同步阻塞一样简单,却能达到异步非阻塞的性能。

// 传统方式:使用固定线程池处理大量请求
ExecutorService executor = Executors.newFixedThreadPool(200);
for (int i = 0; i < 10_000; i++) {
    executor.submit(() -> {
        handleRequest(); // 可能包含阻塞操作
    });
}

// Java 21 方式:每个请求一个虚拟线程
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
    for (int i = 0; i < 10_000; i++) {
        executor.submit(() -> {
            handleRequest(); // 阻塞时自动让出载体线程
        });
    }
}

虚拟线程的核心优势在于:阻塞即廉价。当虚拟线程执行阻塞 I/O 操作(如数据库查询、网络调用)时,它会自动从载体线程上卸载,让载体线程去执行其他虚拟线程,从而大幅提高硬件利用率。


2. 语言特性增强

2.1 记录模式(Record Patterns)——正式特性

记录模式在 Java 19 预览,Java 21 正式转正。它允许在模式匹配中直接解构记录,使代码更简洁。

record Point(int x, int y) {}

void printSum(Object obj) {
    if (obj instanceof Point(int x, int y)) {
        System.out.println(x + y);
    }
}

// 在 switch 中使用
String describe(Object obj) {
    return switch (obj) {
        case Point(int x, int y) -> "Point(" + x + ", " + y + ")";
        case String s -> "String: " + s;
        default -> "Unknown";
    };
}

2.2 Switch 模式匹配(Pattern Matching for switch)——正式特性

Java 21 进一步完善了 switch 的模式匹配,支持更丰富的模式类型,包括类型模式、记录模式、守卫模式等。

String formatter(Object obj) {
    return switch (obj) {
        case Integer i -> String.format("int %d", i);
        case Long l    -> String.format("long %d", l);
        case Double d  -> String.format("double %f", d);
        case String s  -> String.format("String %s", s);
        case null      -> "null value";
        default        -> "Unknown";
    };
}

// 带守卫的模式
void test(Object obj) {
    switch (obj) {
        case String s when s.length() > 5 -> System.out.println("Long string");
        case String s -> System.out.println("Short string");
        default -> {}
    }
}

2.3 字符串模板(String Templates)——预览特性

字符串模板允许在字符串中嵌入表达式,简化字符串拼接。

String name = "Alice";
int age = 30;

// 使用 STR 模板处理器
String message = STR."My name is \{name} and I am \{age} years old.";
// 输出:My name is Alice and I am 30 years old.

// 支持表达式
String info = STR."Next year, \{name} will be \{age + 1}.";

// 自定义模板处理器(例如 JSON 转义)
String json = JSON."""
    {
        "name": "\{name}",
        "age": \{age}
    }
    """;

注意:字符串模板目前是预览特性,需要使用 --enable-preview 启用。

2.4 未命名模式和变量(Unnamed Patterns and Variables)——预览特性

当某些变量或模式在代码中不需要使用时,可以使用下划线 _ 忽略它们,使代码更清晰。

// 未命名变量:忽略不使用的变量
try (var _ = conn.lock()) {
    // 无需使用锁对象
}

// 未命名模式:在记录解构中忽略部分组件
if (obj instanceof Point(int x, _)) {
    System.out.println("x=" + x);
}

// 未命名模式变量:在异常捕获中忽略异常实例
try { ... } catch (Exception _) { ... }

3. 核心库增强

3.1 顺序集合(Sequenced Collections)

Java 21 引入了新的集合接口 SequencedCollectionSequencedSetSequencedMap,为有序集合提供了统一的获取首尾元素、逆序视图等操作。

// 对 List 的操作
SequencedCollection<String> list = new ArrayList<>(List.of("a", "b", "c"));
String first = list.getFirst();  // "a"
String last = list.getLast();    // "c"
list.addFirst("x");  // [x, a, b, c]
list.addLast("z");   // [x, a, b, c, z]
SequencedCollection<String> reversed = list.reversed(); // 逆序视图

// 对 LinkedHashSet 的操作
SequencedSet<String> set = new LinkedHashSet<>(List.of("a", "b", "c"));
String first = set.getFirst();  // "a"
String last = set.getLast();    // "c"

// 对 TreeMap 的操作
SequencedMap<String, Integer> map = new TreeMap<>();
map.put("a", 1);
map.put("b", 2);
Map.Entry<String, Integer> firstEntry = map.firstEntry(); // "a"=1
Map.Entry<String, Integer> lastEntry = map.lastEntry();   // "b"=2

3.2 分代 ZGC(Generational ZGC)

Java 21 将分代模式引入 ZGC,通过区分年轻代和老年代,显著降低了垃圾回收的 CPU 和内存开销。分代 ZGC 默认开启,可通过 -XX:+UseZGC -XX:+ZGenerational 显式启用。

3.3 密钥派生函数 API(Key Derivation Function)

新增 KDF 类,支持基于密码的密钥派生函数(如 HKDF、PBKDF2),简化安全密钥生成。

// 示例:使用 HKDF 派生密钥
KDF kdf = KDF.getInstance("HKDF-SHA256");
SecretKey key = ...;
AlgorithmParameterSpec params = HKDFParameterSpec.extractThenExpand(...);
SecretKey derivedKey = kdf.deriveKey("AES", key, params);

3.4 结构化并发(Structured Concurrency)——预览特性

结构化并发旨在通过结构化任务的方式来管理并发代码,将线程的生命周期与代码块绑定,避免线程泄漏和取消传播问题。

// 使用 StructuredTaskScope 并发调用多个服务
Response handle() throws ExecutionException, InterruptedException {
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        Future<String> user = scope.fork(() -> fetchUser());
        Future<Integer> order = scope.fork(() -> fetchOrder());

        scope.join();            // 等待所有任务完成
        scope.throwIfFailed();   // 如果有任务失败,抛出异常

        return new Response(user.resultNow(), order.resultNow());
    }
}

3.5 作用域值(Scoped Values)——预览特性

作用域值是一种比 ThreadLocal 更安全、更高效的线程间传值方式,特别适合与虚拟线程搭配使用。

// 定义作用域值
final static ScopedValue<String> USER = ScopedValue.newInstance();

// 在作用域内绑定值并执行
ScopedValue.where(USER, "Alice").run(() -> {
    // 在 run 方法及其调用的任何方法中,可通过 USER.get() 获取 "Alice"
    System.out.println(USER.get());
});

4. 孵化器与预览特性预览

4.1 外部函数与内存 API(Foreign Function & Memory API)——第三次预览

继续完善 Panama 项目,提供安全高效地调用本地代码和访问堆外内存的 API。

// 示例:分配堆外内存并操作
try (Arena arena = Arena.ofConfined()) {
    MemorySegment segment = arena.allocate(100);
    segment.set(ValueLayout.JAVA_INT, 0, 42);
    int value = segment.get(ValueLayout.JAVA_INT, 0);
}

4.2 向量 API(Vector API)——第六次孵化

通过 SIMD(单指令多数据)优化向量计算,提升数值运算性能。

FloatVector a = FloatVector.fromArray(FloatVector.SPECIES_256, floatArray1, 0);
FloatVector b = FloatVector.fromArray(FloatVector.SPECIES_256, floatArray2, 0);
FloatVector c = a.mul(b).add(a);
c.intoArray(resultArray, 0);

5. 性能与工具改进

  • 动态 CDS 归档:提升应用启动速度。
  • Java 命令行增强java -source-target 支持直接指定版本。
  • 安全库更新:TLS 1.3 默认开启,废弃 RC4 等弱加密算法。
  • macOS 分发包:支持 macOS 的 .pkg 安装包。

6. 总结与展望

Java 21 作为 LTS 版本,带来了虚拟线程这一革命性特性,彻底改变了 Java 高并发编程的范式。配合记录模式、switch 模式匹配等语言改进,以及结构化并发、作用域值等并发工具,Java 开发者可以编写出更简洁、更健壮、更高性能的应用程序。对于生产环境,从 Java 17 迁移到 Java 21 风险较低且收益明显,特别是对于 I/O 密集型的服务。

未来,Java 将继续以半年一个版本的节奏演进,我们期待在后续版本中看到字符串模板、结构化并发等特性正式转正,以及 Valhalla 项目(值类型)的推进。


参考链接


希望本文能帮助你快速上手 Java 21 的新特性。如果你有任何问题或见解,欢迎在评论区交流讨论!

前端性能优化:图片懒加载的原理与实现 2026-02-27