Java 21作为最新的LTS版本,带来了虚拟线程、增强的模式匹配、字符串模板等重磅特性。本文将深入解析这些新特性,并通过详细的代码示例展示如何在实际开发中应用它们。

Java 21版本概览

Java 21于2023年9月发布,是继Java 17后的又一个重要的长期支持(LTS)版本。与Java 17相比,Java 21进一步完善了现代Java语言的特性和生态系统。

主要特性一览

  • 虚拟线程 (Virtual Threads):革命性的并发编程模型
  • 模式匹配增强 (Pattern Matching):更简洁的类型检查和数据处理
  • 记录模式 (Record Patterns):用于解构记录类实例
  • Switch表达式增强:更强大的模式匹配能力
  • 字符串模板 (String Templates):安全的字符串插值
  • 顺序集合 (Sequenced Collections):统一的集合API
  • 向量API增强:更好的SIMD支持
  • 外部函数和内存API增强:与本地代码更好的互操作

虚拟线程:并发编程的革命

传统线程模型的痛点

在Java 21之前,我们使用平台线程(Platform Threads)进行并发编程:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 传统方式:每个请求一个线程
public class TraditionalServer {
private final ExecutorService executor = Executors.newCachedThreadPool();

public void handleRequest(HttpRequest request) {
executor.submit(() -> {
// 1. 线程创建开销大
// 2. 线程数量受限(通常几千个)
// 3. 上下文切换成本高
processRequest(request);
});
}
}

虚拟线程的解决方案

Java 21引入了虚拟线程,通过在JVM层面实现轻量级线程:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Java 21:使用虚拟线程
public class VirtualThreadServer {
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

public void handleRequest(HttpRequest request) {
executor.submit(() -> {
// 1. 虚拟线程创建几乎无开销
// 2. 可以创建数百万个虚拟线程
// 3. 上下文切换成本极低
processRequest(request);
});
}
}

虚拟线程的核心优势

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 更简洁的API
public class VirtualThreadExamples {

// 方式1:使用ExecutorService
public void example1() {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(this::someTask);
}
}

// 方式2:直接创建虚拟线程
public void example2() {
Thread virtualThread = Thread.ofVirtual()
.name("my-virtual-thread")
.start(() -> {
System.out.println("Running in virtual thread: " + Thread.currentThread());
});

virtualThread.join();
}

// 方式3:使用Thread.Builder
public void example3() {
Thread.Builder builder = Thread.ofVirtual().name("worker-", 0);
Runnable task = () -> System.out.println("Task executed");

// 创建未启动的线程
Thread thread = builder.unstarted(task);
thread.start();
thread.join();
}
}

模式匹配的全面增强

instanceof模式匹配

Java 21进一步简化了模式匹配的语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Java 17及之前的写法
public void processObject(Object obj) {
if (obj instanceof String s) {
System.out.println("String length: " + s.length());
} else if (obj instanceof Integer i) {
System.out.println("Integer value: " + i);
}
}

// Java 21:使用when子句进行卫哨
public void processObject(Object obj) {
switch (obj) {
case String s when s.length() > 5 -> System.out.println("Long string: " + s);
case String s -> System.out.println("Short string: " + s);
case Integer i when i > 100 -> System.out.println("Big integer: " + i);
case Integer i -> System.out.println("Small integer: " + i);
default -> System.out.println("Other type");
}
}

记录模式的强大威力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 定义记录类
public record Person(String name, int age) {}
public record Address(String city, String country) {}
public record Employee(Person person, Address address, double salary) {}

// Java 21:嵌套记录模式匹配
public void processEmployee(Object obj) {
if (obj instanceof Employee(var person, var address, var salary)
&& person instanceof Person(var name, var age)
&& address instanceof Address(var city, var country)) {

System.out.println(name + " from " + city + " earns " + salary);
}
}

// 更简洁的写法:直接解构
public void processEmployee(Employee emp) {
if (emp instanceof Employee(Person(var name, var age), Address(var city, var country), var salary)) {
System.out.println(name + "(" + age + ") from " + city + " earns " + salary);
}
}

Switch表达式的模式匹配

1
2
3
4
5
6
7
8
9
10
11
// Java 21:完整的模式匹配Switch
public static String describeShape(Shape shape) {
return switch (shape) {
case Circle(var radius) when radius > 10 -> "Large circle";
case Circle(var radius) -> "Small circle with radius " + radius;
case Rectangle(var width, var height) when width == height -> "Square";
case Rectangle(var width, var height) -> "Rectangle " + width + "x" + height;
case Triangle(var a, var b, var c) -> "Triangle with sides " + a + "," + b + "," + c;
default -> "Unknown shape";
};
}

字符串模板:安全的字符串处理

传统字符串拼接的问题

1
2
3
4
// 传统方式:容易出错且可读性差
public String createMessage(String name, int age, double salary) {
return "Employee " + name + " (age: " + age + ") earns $" + salary + " per year";
}

字符串模板的解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Java 21:使用字符串模板(预览特性)
public String createMessage(String name, int age, double salary) {
// 使用STR模板处理器
return STR."Employee \{name} (age: \{age}) earns $\{salary} per year";
}

// 自定义模板处理器
public String createHtmlMessage(String name, int age, double salary) {
return HTML."""
<div class="employee">
<h2>\{name}</h2>
<p>Age: \{age}</p>
<p>Salary: $\{salary}</p>
</div>
""";
}

字符串模板的高级用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 多行字符串模板
public String createReport(List<Employee> employees) {
StringBuilder sb = new StringBuilder();
sb.append(STR."""
Employee Report
===============

""");

for (Employee emp : employees) {
sb.append(STR."""
Name: \{emp.name()}
Age: \{emp.age()}
Salary: $\{emp.salary()}

""");
}

return sb.toString();
}

// 模板中的表达式
public String createComplexMessage(String name, LocalDate birthDate) {
int age = Period.between(birthDate, LocalDate.now()).getYears();

return STR."\{name} is \{age} years old and was born on \{birthDate.format(DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM))}";
}

顺序集合:统一的集合API

集合操作的统一接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Java 21之前:不同的集合类型有不同的方法
public void demonstrateCollections() {
List<String> list = List.of("A", "B", "C");
Deque<String> deque = new ArrayDeque<>(List.of("X", "Y", "Z"));
SortedSet<String> sortedSet = new TreeSet<>(Set.of("1", "2", "3"));

// 获取第一个元素 - 方法名不统一
String firstFromList = list.get(0); // list.get(0)
String firstFromDeque = deque.getFirst(); // deque.getFirst()
String firstFromSortedSet = sortedSet.first(); // sortedSet.first()

// 获取最后一个元素 - 方法名不统一
String lastFromList = list.get(list.size() - 1); // 需要计算索引
String lastFromDeque = deque.getLast(); // deque.getLast()
String lastFromSortedSet = sortedSet.last(); // sortedSet.last()
}

// Java 21:统一的顺序集合接口
public void demonstrateSequencedCollections() {
List<String> list = List.of("A", "B", "C");
Deque<String> deque = new ArrayDeque<>(List.of("X", "Y", "Z"));
SortedSet<String> sortedSet = new TreeSet<>(Set.of("1", "2", "3"));

// 统一的API
String firstFromList = list.getFirst(); // 统一的方法名
String firstFromDeque = deque.getFirst(); // 统一的方法名
String firstFromSortedSet = sortedSet.getFirst(); // 统一的方法名

String lastFromList = list.getLast(); // 统一的方法名
String lastFromDeque = deque.getLast(); // 统一的方法名
String lastFromSortedSet = sortedSet.getLast(); // 统一的方法名
}

顺序集合的实际应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 栈和队列的统一操作
public void stackAndQueueOperations() {
// 栈操作
Deque<String> stack = new ArrayDeque<>();
stack.addFirst("First"); // 压栈
stack.addFirst("Second");
String top = stack.getFirst(); // 查看栈顶
String popped = stack.removeFirst(); // 出栈

// 队列操作
Deque<String> queue = new ArrayDeque<>();
queue.addLast("First"); // 入队
queue.addLast("Second");
String front = queue.getFirst(); // 查看队首
String dequeued = queue.removeFirst(); // 出队
}

// 反转集合
public void reverseOperations() {
List<String> list = new ArrayList<>(List.of("A", "B", "C"));
Deque<String> deque = new ArrayDeque<>(List.of("X", "Y", "Z"));

// 反转操作
List<String> reversedList = list.reversed(); // List.reversed()
Deque<String> reversedDeque = deque.reversed(); // Deque.reversed()
}

向量API的增强

SIMD操作的基础

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Java 21:增强的向量API
public class VectorOperations {

// 向量加法
public void vectorAddition() {
int[] a = {1, 2, 3, 4, 5, 6, 7, 8};
int[] b = {8, 7, 6, 5, 4, 3, 2, 1};
int[] result = new int[8];

// 使用向量API进行SIMD操作
Vector<Integer> va = IntVector.fromArray(IntVector.SPECIES_PREFERRED, a, 0);
Vector<Integer> vb = IntVector.fromArray(IntVector.SPECIES_PREFERRED, b, 0);
Vector<Integer> vc = va.add(vb);
vc.intoArray(result, 0);

System.out.println(Arrays.toString(result)); // [9, 9, 9, 9, 9, 9, 9, 9]
}

// 向量点积
public int vectorDotProduct() {
float[] a = {1.0f, 2.0f, 3.0f, 4.0f};
float[] b = {4.0f, 3.0f, 2.0f, 1.0f};

Vector<Float> va = FloatVector.fromArray(FloatVector.SPECIES_128, a, 0);
Vector<Float> vb = FloatVector.fromArray(FloatVector.SPECIES_128, b, 0);

return va.mul(vb).reduceLanesToLong(FloatVector.REDUCE_SUM);
}
}

外部函数和内存API的增强

与本地代码的互操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Java 21:增强的外部函数API
public class NativeLibraryExample {

// 定义外部函数
public static class NativeMath {
public static final ForeignFunction sqrt = ForeignFunction.of(
"sqrt",
MethodType.methodType(double.class, double.class),
Linker.nativeLinker().defaultLookup().find("sqrt").orElseThrow()
);

public static double nativeSqrt(double value) throws Throwable {
return (double) sqrt.call(value);
}
}

// 使用示例
public void demonstrateNativeCall() throws Throwable {
double result = NativeMath.nativeSqrt(16.0);
System.out.println("sqrt(16.0) = " + result); // 输出: 4.0
}
}

相对Java 17的主要改动

1. 并发编程模型的革新

  • 改动:引入虚拟线程,改变传统的线程模型
  • 影响:开发者可以创建数百万并发任务,而不会耗尽系统资源
  • 兼容性:完全向后兼容,现有代码无需修改

2. 模式匹配的全面升级

  • 改动:从简单的instanceof模式匹配扩展到完整的模式匹配系统
  • 影响:代码更加简洁,类型安全得到增强
  • 新特性:记录模式、卫哨模式、Switch表达式中的模式匹配

3. 集合API的统一

  • 改动:引入SequencedCollection接口,统一不同集合类型的操作
  • 影响:API更加一致,减少学习成本
  • 受益者:所有使用集合的Java开发者

4. 字符串处理的现代化

  • 改动:引入字符串模板,提供类型安全的字符串插值
  • 影响:减少字符串拼接错误,提高代码可读性
  • 安全性:模板处理器可以验证和转换插值内容

5. 性能和底层能力的增强

  • 改动:向量API和外部函数API的持续改进
  • 影响:更好的硬件利用率,更强的本地代码互操作能力
  • 应用场景:高性能计算、系统级编程

Java 21的优势与设计哲学

1. 生产力提升

Java 21的设计哲学是让开发者能够用更少的代码做更多的事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Java 17的写法
public String formatEmployee(Employee emp) {
if (emp == null) return "N/A";
return emp.name() + " (" + emp.age() + ") - $" + emp.salary();
}

// Java 21的写法
public String formatEmployee(Employee emp) {
return switch (emp) {
case null -> "N/A";
case Employee(var name, var age, var salary) ->
STR."\{name} (\{age}) - $\{salary}";
};
}

2. 性能优势

  • 虚拟线程:支持数百万并发任务,内存使用更高效
  • 向量API:利用SIMD指令,数值计算性能显著提升
  • 垃圾回收器改进:更好的内存管理和GC性能

3. 生态系统成熟度

Java 21作为LTS版本,得到了广泛的工具和框架支持:

  • Spring Framework 6.x原生支持虚拟线程
  • 主流应用服务器(如Tomcat、Jetty)已适配
  • 构建工具(Maven、Gradle)完整支持

4. 向后兼容性保证

Java 21保持了极高的向后兼容性:

  • 现有代码无需修改即可运行
  • 渐进式采用新特性
  • 平滑的迁移路径

迁移策略与最佳实践

1. 渐进式迁移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 第一阶段:保持现有代码不变
public class MigrationExample {

// 原有代码继续使用平台线程
private final ExecutorService oldExecutor = Executors.newCachedThreadPool();

// 新代码使用虚拟线程
private final ExecutorService newExecutor = Executors.newVirtualThreadPerTaskExecutor();

public void migrateGradually() {
// 对于I/O密集型任务,优先使用虚拟线程
newExecutor.submit(this::ioIntensiveTask);

// 对于CPU密集型任务,继续使用平台线程
oldExecutor.submit(this::cpuIntensiveTask);
}
}

2. 性能监控和调优

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 使用Java 21的监控特性
public class PerformanceMonitoring {

public void monitorVirtualThreads() {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();

// 监控虚拟线程
long virtualThreadCount = Thread.getAllStackTraces().keySet()
.stream()
.filter(Thread::isVirtual)
.count();

System.out.println("Active virtual threads: " + virtualThreadCount);
}

public void monitorVectorOperations() {
// 使用Stopwatch监控向量操作性能
Stopwatch stopwatch = Stopwatch.createStarted();

// 执行向量操作
performVectorComputation();

stopwatch.stop();
System.out.println("Vector operation took: " + stopwatch.elapsed());
}
}

3. 错误处理增强

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Java 21:更好的错误处理
public class ErrorHandlingExample {

public Result<String, Exception> processData(String input) {
try {
// 使用新的模式匹配进行数据验证
return switch (validateInput(input)) {
case ValidData(var data) -> Result.success(processValidData(data));
case InvalidData(var error) -> Result.failure(new ValidationException(error));
};
} catch (Exception e) {
return Result.failure(e);
}
}

// 记录模式的错误处理
public void handleResult(Result<String, Exception> result) {
if (result instanceof Success(var value)) {
System.out.println("Success: " + value);
} else if (result instanceof Failure(var error)) {
System.err.println("Error: " + error.getMessage());
}
}
}

总结与展望

Java 21作为现代Java生态的里程碑版本,带来了以下核心价值:

技术进步

  1. 并发编程的革命:虚拟线程让高并发编程变得简单而高效
  2. 语言表达力的提升:模式匹配和字符串模板让代码更加简洁
  3. API设计的统一:顺序集合接口消除了集合操作的不一致性
  4. 性能的持续优化:向量API和底层改进带来更好的硬件利用率

开发体验改善

  1. 代码简洁性:同样的功能需要更少的代码
  2. 类型安全性:编译时错误检查更加严格
  3. 可维护性:统一的API和更好的抽象让代码更容易维护
  4. 学习曲线:新特性建立在现有概念基础上,易于学习

生态系统影响

  1. 框架适配:主流框架正在快速适配Java 21的新特性
  2. 工具支持:IDE、构建工具、监控工具的完善支持
  3. 社区采用:越来越多的项目开始使用Java 21作为运行环境

未来展望

Java 21不仅是一个技术版本的更新,更是Java语言现代化进程中的重要一步。随着虚拟线程、模式匹配等特性的成熟,Java将继续在企业级应用、云计算、高性能计算等领域发挥重要作用。

对于开发者而言,Java 21提供了强大的工具来应对现代软件开发的挑战:

  • 微服务架构中的高并发需求
  • 大数据处理中的性能要求
  • 云原生应用的可观测性需求
  • 现代化代码的简洁性和可维护性

建议开发者积极尝试Java 21的新特性,在保证系统稳定的前提下,逐步迁移到这个功能丰富、性能优异的LTS版本。

参考资料

  1. OpenJDK Java 21 Release Notes
  2. JEP 444: Virtual Threads
  3. JEP 440: Record Patterns
  4. JEP 441: Pattern Matching for switch
  5. JEP 430: String Templates (Preview)
  6. JEP 431: Sequenced Collections
  7. State of Java Survey 2023