知识模块
☕ Java 知识模块
六、Java 新特性
JDK21 新特性

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=2

3. 记录模式(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 MyApp

6. 字符串模板(预览)

// 传统字符串拼接
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 模式匹配?类型匹配 + 守卫条件

参考资料