JDK 21 新特性
面试提问
"JDK 21 有哪些重要新特性?虚拟线程是什么?"
JDK 21 简介
JDK 21 是 LTS(长期支持)版本,引入了多项重要特性。
核心特性
| 特性 | 状态 |
|---|---|
| 虚拟线程 | ✅ 正式 |
| 序列集合 | ✅ 正式 |
| 记录模式 | ✅ 正式 |
| Switch 模式匹配 | ✅ 正式 |
| 分代 ZGC | ✅ 正式 |
| 字符串模板 | 预览 |
| 未命名类 | 预览 |
1. 虚拟线程(Virtual Threads)
概念
虚拟线程是轻量级线程,由 JVM 调度,而非操作系统。
| 对比项 | 平台线程 | 虚拟线程 |
|---|---|---|
| 创建成本 | 高 | 极低 |
| 内存占用 | ~1MB | ~1KB |
| 数量限制 | 数千 | 数百万 |
| 调度者 | 操作系统 | JVM |
创建虚拟线程
// 方式一:Thread.startVirtualThread()
Thread vt = Thread.startVirtualThread(() -> {
System.out.println("Hello Virtual Thread");
});
// 方式二:Thread.ofVirtual()
Thread vt = Thread.ofVirtual().start(() -> {
System.out.println("Hello");
});
// 方式三:Executors
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> "Hello");
}
// 方式四:ThreadFactory
ThreadFactory factory = Thread.ofVirtual().factory();
Thread vt = factory.newThread(() -> {});高并发示例
// 传统线程池:受限
try (var executor = Executors.newFixedThreadPool(200)) {
for (int i = 0; i < 10000; i++) {
executor.submit(() -> {
Thread.sleep(1000); // 阻塞
return "Done";
});
}
} // 200 个线程,10000 个任务排队
// 虚拟线程:无压力
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10000; i++) {
executor.submit(() -> {
Thread.sleep(1000); // 阻塞但挂起虚拟线程
return "Done";
});
}
} // 10000 个虚拟线程并行注意事项
// 不要与平台线程池混用
// ❌ 错误:虚拟线程执行 CPU 密集任务
Executors.newVirtualThreadPerTaskExecutor()
.submit(() -> fibonacci(40)); // 占用 CPU,阻塞载体线程
// ✅ 正确:虚拟线程适合 IO 密集任务
Executors.newVirtualThreadPerTaskExecutor()
.submit(() -> httpClient.send(request)); // IO 等待时不占 CPU
// synchronized 问题:会钉住载体线程
// ❌ 避免
public synchronized void method() {
// 虚拟线程进入 synchronized 方法时会被钉住
}
// ✅ 使用 ReentrantLock
private final Lock lock = new ReentrantLock();
public void method() {
lock.lock();
try {
// 虚拟线程可以正常挂起
} finally {
lock.unlock();
}
}2. 序列集合(Sequenced Collections)
概念
提供统一的接口来访问集合的首尾元素。
新接口
// SequencedCollection:有序集合
interface SequencedCollection<E> extends Collection<E> {
SequencedCollection<E> reversed(); // 反转视图
void addFirst(E e); // 添加到头部
void addLast(E e); // 添加到尾部
E getFirst(); // 获取首元素
E getLast(); // 获取尾元素
E removeFirst(); // 移除首元素
E removeLast(); // 移除尾元素
}
// SequencedSet:有序 Set
interface SequencedSet<E> extends Set<E>, SequencedCollection<E> { }
// SequencedMap:有序 Map
interface SequencedMap<K, V> extends Map<K, V> {
SequencedMap<K, V> reversed();
SequencedSet<K> sequencedKeySet();
SequencedCollection<V> sequencedValues();
SequencedSet<Entry<K, V>> sequencedEntrySet();
V putFirst(K k, V v);
V putLast(K k, V v);
Entry<K, V> firstEntry();
Entry<K, V> lastEntry();
Entry<K, V> pollFirstEntry();
Entry<K, V> pollLastEntry();
}使用示例
// ArrayList
ArrayList<Integer> list = new ArrayList<>(List.of(1, 2, 3, 4, 5));
list.getFirst(); // 1
list.getLast(); // 5
list.addFirst(0); // [0, 1, 2, 3, 4, 5]
list.addLast(6); // [0, 1, 2, 3, 4, 5, 6]
list.reversed(); // [6, 5, 4, 3, 2, 1, 0]
// LinkedList
LinkedList<String> linked = new LinkedList<>();
linked.addFirst("a");
// LinkedHashSet
LinkedHashSet<String> set = new LinkedHashSet<>();
set.addFirst("first");
set.addLast("last");
// LinkedHashMap
LinkedHashMap<String, Integer> map = new LinkedHashMap<>();
map.putFirst("a", 1);
map.putLast("b", 2);
map.firstEntry(); // a=1
map.lastEntry(); // b=23. 记录模式(Record Patterns)
概念
在模式匹配中解构 Record。
record Point(int x, int y) { }
// JDK 16
if (obj instanceof Point p) {
int x = p.x();
int y = p.y();
}
// JDK 21:解构
if (obj instanceof Point(int x, int y)) {
System.out.println(x + ", " + y);
}
// 嵌套解构
record Rectangle(Point upperLeft, Point lowerRight) { }
if (obj instanceof Rectangle(Point(int x1, int y1), Point(int x2, int y2))) {
// 直接获取四个坐标
}
// switch 中使用
switch (obj) {
case Point(int x, int y) -> System.out.println(x + y);
default -> System.out.println("Not a point");
}4. Switch 模式匹配
完整示例
static String formatter(Object obj) {
return switch (obj) {
case Integer i -> "int: " + i;
case Long l -> "long: " + l;
case Double d -> "double: " + d;
case String s -> "string: " + s;
default -> "unknown";
};
}
// 带守卫条件
static String categorize(Number n) {
return switch (n) {
case Integer i && i < 0 -> "negative int";
case Integer i && i > 0 -> "positive int";
case Integer i -> "zero";
case Double d -> "double";
default -> "unknown";
};
}
// 结合 Record 模式
sealed interface Shape permits Circle, Rectangle { }
record Circle(int radius) implements Shape { }
record Rectangle(int width, int height) implements Shape { }
static double area(Shape shape) {
return switch (shape) {
case Circle(int r) -> Math.PI * r * r;
case Rectangle(int w, int h) -> w * h;
};
}5. 分代 ZGC
特点
- ZGC 变成分代式,区分年轻代和老年代
- 显著降低延迟
- 更好的内存利用率
启用方式
# JDK 21 默认启用
java -XX:+UseZGC MyApp
# 指定堆大小
java -XX:+UseZGC -Xms4g -Xmx4g MyApp6. 字符串模板(预览)
// 传统字符串拼接
String name = "张三";
int age = 25;
String s = "姓名: " + name + ", 年龄: " + age;
// 字符串模板(JDK 21 预览)
String s = STR."姓名: \{name}, 年龄: \{age}";
// 多行模板
String json = STR."""
{
"name": "\{name}",
"age": \{age}
}
""";
// 自定义模板处理器
StringTableProcessor STR = StringTableProcessor.INSTANCE;7. 未命名类(预览)
// 传统:需要 class 包装
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello");
}
}
// JDK 21 预览:顶级 main
void main() {
System.out.println("Hello");
}面试要点总结
| 问题 | 答案要点 |
|---|---|
| 虚拟线程? | 轻量级线程,JVM 调度,适合 IO 密集 |
| 虚拟线程数量? | 可创建数百万个 |
| 序列集合? | 统一访问首尾元素 |
| 记录模式? | 解构 Record |
| Switch 模式匹配? | 类型匹配 + 守卫条件 |