各版本JDK对比:JDK 21 特性详解

4 阅读10分钟

JDK 21 特性详解

1. 概述

JDK 21(2023年9月发布)是Java的长期支持(LTS)版本,引入了多项重要特性和性能改进。作为继JDK 17之后的又一个LTS版本,JDK 21在保持兼容性的同时,提供了更现代、更高效、更简洁的语言特性和API,为Java开发者带来了更好的开发体验和性能表现。

2. 核心特性详解

2.1 虚拟线程 (Virtual Threads)

特性说明:

  • 轻量级线程,由JVM管理,不是操作系统线程
  • 支持千万级并发,启动时间微秒级
  • 与现有代码兼容,无需修改即可使用
  • 减少了线程上下文切换开销
  • 自动挂起/恢复,适合IO密集型任务

核心概念:

  • 虚拟线程:由JVM管理的用户态线程,映射到操作系统线程
  • 载体线程:操作系统线程,用于执行虚拟线程
  • 调度器:JVM内置的调度器,负责虚拟线程的调度

创建方式:

  1. Thread.startVirtualThread():直接启动一个虚拟线程
  2. Thread.ofVirtual():创建虚拟线程构建器
  3. Executors.newVirtualThreadPerTaskExecutor():创建虚拟线程执行器

代码示例:

public static void demonstrateVirtualThreads() throws Exception {
    System.out.println("=== 虚拟线程示例 ===");

    // 创建虚拟线程的几种方式
    System.out.println("1. 使用Thread.startVirtualThread");
    Thread virtualThread1 = Thread.startVirtualThread(() -> {
        System.out.println("虚拟线程执行: " + Thread.currentThread());
    });
    virtualThread1.join();

    System.out.println("\n2. 使用Thread.ofVirtual()");
    Thread virtualThread2 = Thread.ofVirtual()
            .name("my-virtual-thread-", 0)
            .start(() -> {
                System.out.println("命名的虚拟线程: " + Thread.currentThread().getName());
            });
    virtualThread2.join();

    System.out.println("\n3. 使用Executors.newVirtualThreadPerTaskExecutor");
    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        List<Future<String>> futures = new ArrayList<>();

        for (int i = 1; i <= 5; i++) {
            final int taskId = i;
            Future<String> future = executor.submit(() -> {
                Thread.sleep(100); // 模拟IO操作
                return "任务 " + taskId + " 完成 by " + Thread.currentThread();
            });
            futures.add(future);
        }

        // 获取结果
        for (Future<String> future : futures) {
            System.out.println(future.get());
        }
    }

    // 演示虚拟线程的轻量级特性
    System.out.println("\n4. 创建大量虚拟线程");
    long startTime = System.currentTimeMillis();

    int numberOfThreads = 10000;
    CountDownLatch latch = new CountDownLatch(numberOfThreads);

    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        for (int i = 0; i < numberOfThreads; i++) {
            final int threadId = i;
            executor.submit(() -> {
                try {
                    Thread.sleep(100); // 模拟IO等待
                    System.out.print(".");
                    if (threadId % 1000 == 0) {
                        System.out.println(" [" + threadId + "]");
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    latch.countDown();
                }
            });
        }

        latch.await(); // 等待所有任务完成
    }

    long endTime = System.currentTimeMillis();
    System.out.println("\n创建 " + numberOfThreads + " 个虚拟线程耗时: " +
            (endTime - startTime) + "ms");
}

性能优势:

  • 创建和启动速度比传统线程快100倍以上
  • 内存占用比传统线程低100-1000倍
  • 减少了操作系统线程调度开销
  • 适合高并发IO密集型应用,如Web服务器
  • 避免了传统线程池的上下文切换开销
  • 支持千万级并发,远超传统线程的数万级

适用场景:

  • IO密集型应用(网络请求、数据库操作、文件IO)
  • 高并发Web服务器
  • 微服务架构
  • 事件驱动应用
  • 需要大量并发任务的场景

2.2 记录模式 (Record Patterns)

特性说明:

  • 允许在模式匹配中直接解构记录类
  • 支持嵌套记录模式
  • 减少了样板代码,提高了可读性
  • 支持在switch和if语句中使用

核心概念:

  • 记录类:使用record关键字定义的不可变类
  • 记录模式:在模式匹配中解构记录类的模式
  • 嵌套记录模式:解构嵌套的记录类

代码示例:

// 记录类定义
record Point(int x, int y) {
    public double distanceTo(Point other) {
        int dx = x - other.x;
        int dy = y - other.y;
        return Math.sqrt(dx * dx + dy * dy);
    }
}

record CircleRecord(Point center, double radius) {
    public double area() {
        return Math.PI * radius * radius;
    }
}

record Position(Point point) {
    // 包装类示例
}

record RectangleRecord(Position upperLeft, Position lowerRight) {
    public double width() {
        return Math.abs(lowerRight.point().x() - upperLeft.point().x());
    }

    public double height() {
        return Math.abs(lowerRight.point().y() - upperLeft.point().y());
    }

    public double area() {
        return width() * height();
    }
}

// 使用记录模式
public static void demonstrateRecordPatterns() {
    System.out.println("\n=== 记录模式示例 ===");

    List<Object> objects = Arrays.asList(
            new Point(10, 20),
            new CircleRecord(new Point(5, 5), 8.0),
            "Just a string",
            42
    );

    for (Object obj : objects) {
        String description = processObject(obj);
        System.out.println(description);
    }

    // 嵌套记录模式
    System.out.println("\n--- 嵌套记录模式 ---");
    RectangleRecord rect = new RectangleRecord(
            new Position(new Point(0, 0)),
            new Position(new Point(10, 10))
    );

    if (rect instanceof RectangleRecord(Position(Point upperLeft), Position(Point lowerRight))) {
        System.out.println("矩形: 左上角(" + upperLeft.x() + "," + upperLeft.y() +
                "), 右下角(" + lowerRight.x() + "," + lowerRight.y() + ")");
    }
}

private static String processObject(Object obj) {
    // 记录模式在switch中的使用
    return switch (obj) {
        case Point(int x, int y) ->
                String.format("点坐标: (%d, %d)", x, y);

        case CircleRecord(Point center, double radius) ->
                String.format("圆形 - 中心(%d, %d), 半径%.1f",
                        center.x(), center.y(), radius);

        case String s ->
                "字符串: " + s;

        case Integer i ->
                "整数: " + i;

        case null ->
                "空对象";

        default ->
                "未知对象: " + obj.getClass().getSimpleName();
    };
}

性能优势:

  • 减少了记录类字段访问的样板代码
  • 编译器可以进行更积极的优化
  • 减少了运行时的类型检查和转换开销
  • 提高了代码的可维护性
  • 支持更简洁、更表达力强的代码

适用场景:

  • 处理记录类的场景
  • 复杂的数据结构解构
  • 模式匹配操作
  • 减少样板代码的场景

2.3 有序集合 (Sequenced Collections)

特性说明:

  • 提供了统一的有序集合API
  • 引入了SequencedCollection、SequencedSet、SequencedMap接口
  • 支持在集合开头和结尾高效操作
  • 提供了统一的reverse()方法获取反转视图
  • 与现有集合框架兼容

核心接口:

  • SequencedCollection:有序集合的基本接口
  • SequencedSet:有序集合的子接口,添加了Set特性
  • SequencedMap:有序映射的接口

核心方法:

  • getFirst():获取第一个元素
  • getLast():获取最后一个元素
  • addFirst():在开头添加元素
  • addLast():在结尾添加元素
  • removeFirst():移除第一个元素
  • removeLast():移除最后一个元素
  • reversed():获取反转视图
  • putFirst():在映射开头添加键值对
  • putLast():在映射结尾添加键值对
  • firstEntry():获取第一个键值对
  • lastEntry():获取最后一个键值对

代码示例:

public static void demonstrateSequencedCollections() {
    System.out.println("\n=== 有序集合示例 ===");

    // SequencedSet
    System.out.println("--- SequencedSet ---");
    SequencedSet<String> sequencedSet = new LinkedHashSet<>();
    sequencedSet.addAll(List.of("A", "B", "C", "D"));

    System.out.println("原始顺序: " + sequencedSet);
    System.out.println("第一个元素: " + sequencedSet.getFirst());
    System.out.println("最后一个元素: " + sequencedSet.getLast());
    System.out.println("反转视图: " + sequencedSet.reversed());

    // SequencedMap
    System.out.println("\n--- SequencedMap ---");
    SequencedMap<Integer, String> sequencedMap = new LinkedHashMap<>();
    sequencedMap.put(1, "一");
    sequencedMap.put(2, "二");
    sequencedMap.put(3, "三");

    System.out.println("原始顺序: " + sequencedMap);
    System.out.println("第一个条目: " + sequencedMap.firstEntry());
    System.out.println("最后一个条目: " + sequencedMap.lastEntry());
    System.out.println("反转视图: " + sequencedMap.reversed());

    // 在开头和结尾操作
    sequencedMap.putFirst(0, "零");
    sequencedMap.putLast(4, "四");
    System.out.println("操作后: " + sequencedMap);
}

性能优势:

  • 提供了高效的首尾元素访问和操作
  • 减少了不同集合类型之间的API差异
  • 统一的反转操作,避免了自定义实现的开销
  • 提高了代码的可读性和可维护性
  • 与现有集合框架完全兼容

适用场景:

  • 需要频繁访问首尾元素的场景
  • 需要反转集合的场景
  • 需要在集合首尾添加/移除元素的场景
  • 提高代码可读性的场景

2.4 其他重要特性

2.4.1 模式匹配for循环
  • 增强的for循环,支持模式匹配
  • 简化了集合元素的处理
  • 减少了样板代码
  • 提高了代码的可读性
2.4.2 字符串模板 (预览)
  • 更灵活的字符串格式化
  • 支持表达式嵌入
  • 减少了字符串拼接的样板代码
  • 提高了代码的可读性和可维护性
2.4.3 向量API(第六孵化器)
  • 提供了用于数值计算的向量操作API
  • 支持SIMD(单指令多数据)操作,提高数值计算性能
  • 适用于科学计算、图像处理、机器学习等场景
  • 第六孵化器版本,进一步完善和优化

3. 性能提升总结

特性性能提升适用场景
虚拟线程创建和启动速度比传统线程快100倍以上,内存占用低100-1000倍IO密集型应用、高并发Web服务器
记录模式减少了记录类字段访问的样板代码,编译器可进行更积极的优化处理记录类、复杂数据结构解构
有序集合提供了高效的首尾元素访问和操作,统一了API需要频繁访问首尾元素的场景
模式匹配for循环减少了样板代码,提高了代码可读性集合元素处理
字符串模板减少了字符串拼接的样板代码字符串格式化
向量API支持SIMD操作,提高数值计算性能科学计算、图像处理、机器学习

4. 最佳实践

4.1 虚拟线程使用建议

  • 优先在IO密集型任务中使用虚拟线程
  • 使用Executors.newVirtualThreadPerTaskExecutor()创建虚拟线程执行器
  • 避免在虚拟线程中执行CPU密集型任务
  • 利用虚拟线程的轻量级特性,处理大量并发任务
  • 注意虚拟线程的自动挂起/恢复机制

4.2 记录模式使用建议

  • 合理使用记录模式解构记录类
  • 利用嵌套记录模式处理复杂数据结构
  • 在switch和if语句中使用记录模式
  • 保持模式匹配的简洁性
  • 注意记录模式的类型安全

4.3 有序集合使用建议

  • 利用统一的API处理有序集合
  • 优先使用getFirst()getLast()等方法访问首尾元素
  • 使用reversed()方法获取反转视图
  • 利用addFirst()addLast()等方法在首尾添加元素
  • 注意有序集合的性能特性

4.4 其他特性使用建议

  • 探索字符串模板的使用,简化字符串格式化
  • 对于数值计算密集的应用,考虑使用向量API
  • 利用模式匹配for循环简化集合处理
  • 关注预览特性的演进,为未来版本做好准备

5. 代码示例

5.1 完整示例类

package com.java.learning;

import java.util.*;
import java.util.concurrent.*;
import java.time.*;

public class JDK21Features {
    public static void main(String[] args) throws Exception {
        demonstrateVirtualThreads();
        demonstrateRecordPatterns();
        demonstrateSequencedCollections();
    }

    public static void demonstrateVirtualThreads() throws Exception {
        System.out.println("=== 虚拟线程示例 ===");

        // 创建虚拟线程的几种方式
        System.out.println("1. 使用Thread.startVirtualThread");
        Thread virtualThread1 = Thread.startVirtualThread(() -> {
            System.out.println("虚拟线程执行: " + Thread.currentThread());
        });
        virtualThread1.join();

        System.out.println("\n2. 使用Thread.ofVirtual()");
        Thread virtualThread2 = Thread.ofVirtual()
                .name("my-virtual-thread-", 0)
                .start(() -> {
                    System.out.println("命名的虚拟线程: " + Thread.currentThread().getName());
                });
        virtualThread2.join();

        System.out.println("\n3. 使用Executors.newVirtualThreadPerTaskExecutor");
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<String>> futures = new ArrayList<>();

            for (int i = 1; i <= 5; i++) {
                final int taskId = i;
                Future<String> future = executor.submit(() -> {
                    Thread.sleep(100); // 模拟IO操作
                    return "任务 " + taskId + " 完成 by " + Thread.currentThread();
                });
                futures.add(future);
            }

            // 获取结果
            for (Future<String> future : futures) {
                System.out.println(future.get());
            }
        }

        // 演示虚拟线程的轻量级特性
        System.out.println("\n4. 创建大量虚拟线程");
        long startTime = System.currentTimeMillis();

        int numberOfThreads = 10000;
        CountDownLatch latch = new CountDownLatch(numberOfThreads);

        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < numberOfThreads; i++) {
                final int threadId = i;
                executor.submit(() -> {
                    try {
                        Thread.sleep(100); // 模拟IO等待
                        System.out.print(".");
                        if (threadId % 1000 == 0) {
                            System.out.println(" [" + threadId + "]");
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    } finally {
                        latch.countDown();
                    }
                });
            }

            latch.await(); // 等待所有任务完成
        }

        long endTime = System.currentTimeMillis();
        System.out.println("\n创建 " + numberOfThreads + " 个虚拟线程耗时: " +
                (endTime - startTime) + "ms");
    }

    public static void demonstrateRecordPatterns() {
        System.out.println("\n=== 记录模式示例 ===");

        List<Object> objects = Arrays.asList(
                new Point(10, 20),
                new CircleRecord(new Point(5, 5), 8.0),
                "Just a string",
                42
        );

        for (Object obj : objects) {
            String description = processObject(obj);
            System.out.println(description);
        }

        // 嵌套记录模式
        System.out.println("\n--- 嵌套记录模式 ---");
        RectangleRecord rect = new RectangleRecord(
                new Position(new Point(0, 0)),
                new Position(new Point(10, 10))
        );

        if (rect instanceof RectangleRecord(Position(Point upperLeft), Position(Point lowerRight))) {
            System.out.println("矩形: 左上角(" + upperLeft.x() + "," + upperLeft.y() +
                    "), 右下角(" + lowerRight.x() + "," + lowerRight.y() + ")");
        }
    }

    private static String processObject(Object obj) {
        // 记录模式在switch中的使用
        return switch (obj) {
            case Point(int x, int y) ->
                    String.format("点坐标: (%d, %d)", x, y);

            case CircleRecord(Point center, double radius) ->
                    String.format("圆形 - 中心(%d, %d), 半径%.1f",
                            center.x(), center.y(), radius);

            case String s ->
                    "字符串: " + s;

            case Integer i ->
                    "整数: " + i;

            case null ->
                    "空对象";

            default ->
                    "未知对象: " + obj.getClass().getSimpleName();
        };
    }

    public static void demonstrateSequencedCollections() {
        System.out.println("\n=== 有序集合示例 ===");

        // SequencedSet
        System.out.println("--- SequencedSet ---");
        SequencedSet<String> sequencedSet = new LinkedHashSet<>();
        sequencedSet.addAll(List.of("A", "B", "C", "D"));

        System.out.println("原始顺序: " + sequencedSet);
        System.out.println("第一个元素: " + sequencedSet.getFirst());
        System.out.println("最后一个元素: " + sequencedSet.getLast());
        System.out.println("反转视图: " + sequencedSet.reversed());

        // SequencedMap
        System.out.println("\n--- SequencedMap ---");
        SequencedMap<Integer, String> sequencedMap = new LinkedHashMap<>();
        sequencedMap.put(1, "一");
        sequencedMap.put(2, "二");
        sequencedMap.put(3, "三");

        System.out.println("原始顺序: " + sequencedMap);
        System.out.println("第一个条目: " + sequencedMap.firstEntry());
        System.out.println("最后一个条目: " + sequencedMap.lastEntry());
        System.out.println("反转视图: " + sequencedMap.reversed());

        // 在开头和结尾操作
        sequencedMap.putFirst(0, "零");
        sequencedMap.putLast(4, "四");
        System.out.println("操作后: " + sequencedMap);
    }
}

// 记录类定义
record Point(int x, int y) {
    public double distanceTo(Point other) {
        int dx = x - other.x;
        int dy = y - other.y;
        return Math.sqrt(dx * dx + dy * dy);
    }
}

record CircleRecord(Point center, double radius) {
    public double area() {
        return Math.PI * radius * radius;
    }
}

record Position(Point point) {
    // 包装类示例
}

record RectangleRecord(Position upperLeft, Position lowerRight) {
    public double width() {
        return Math.abs(lowerRight.point().x() - upperLeft.point().x());
    }

    public double height() {
        return Math.abs(lowerRight.point().y() - upperLeft.point().y());
    }

    public double area() {
        return width() * height();
    }
}

6. 总结

JDK 21作为长期支持(LTS)版本,引入了多项重要特性和性能改进,为Java开发者带来了更好的开发体验和性能表现。从虚拟线程到记录模式,从有序集合到字符串模板,这些特性不仅使代码更简洁、更易读,还显著提升了应用程序的性能。

通过合理使用JDK 21的新特性,开发者可以:

  • 处理千万级并发,远超传统线程的数万级
  • 简化记录类的处理,减少样板代码
  • 统一有序集合的API,提高代码可读性
  • 利用字符串模板简化字符串格式化
  • 提升数值计算性能

JDK 21的发布标志着Java平台的持续演进,为Java在现代应用开发中保持竞争力奠定了基础。对于企业应用和长期项目,升级到JDK 21可以获得更好的性能、更丰富的功能和更长的支持周期,是一个值得考虑的选择。

此外,JDK 21中的预览特性也为未来版本的发展指明了方向,开发者可以通过关注这些特性,为未来的Java版本做好准备,保持技术的先进性。