JDK 21 并发编程

前言

Java 的并发并发模型在 JDK 21 迎来了 “革命性升级”,核心是解决传统并发编程的两大痛点:线程资源昂贵导致的高并发瓶颈和多任务协作混乱导致的代码复杂。这两个问题,JDK 21 分别用 “虚拟线程” 和 “结构化并发” 给出了答案。


一、虚拟线程(Virtual Threads):让 “多线程” 不再昂贵

传统并发的痛点:线程是 “重量级资源”
传统 Java 线程(平台线程)直接对应操作系统线程,创建一个线程要占用 1MB 左右内存,且操作系统能同时运行的线程数量有限(通常几千个)。如果遇到高并发场景(比如 10 万用户同时访问接口),线程池会瞬间耗尽,导致请求排队甚至失败。

比如下面的代码,用线程池处理 10 万任务,会因线程不足变得很慢:

// 传统线程池(最多 1000 个平台线程)
ExecutorService executor = Executors.newFixedThreadPool(1000);

// 模拟 10 万次 I/O 操作(如数据库查询、网络请求)
for (int i = 0; i < 100000; i++) {
    executor.submit(() -> {
        // 模拟 I/O 等待(比如查数据库)
        Thread.sleep(100); // 此时线程完全空闲,但仍占用资源
        return null;
    });
}

原因:10 万任务要排队等待 1000 个线程,大部分时间线程在 “空等” I/O 结果,却浪费着宝贵的系统资源。

虚拟线程的解决方案:轻量级线程,数量 “几乎无限”

虚拟线程是 JVM 管理的 “轻量级线程”,不直接绑定操作系统线程。一个平台线程可以 “承载” 成千上万个虚拟线程,且创建成本极低(内存仅几 KB)。

核心优势:当虚拟线程执行 I/O 操作(如 sleep、数据库查询)时,JVM 会 “暂时释放” 它占用的平台线程,让其他虚拟线程使用。等 I/O 完成后,再重新分配一个平台线程继续运行。

// 虚拟线程池(自动管理,数量无上限)
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

// 同样处理 10 万次 I/O 操作
for (int i = 0; i < 100000; i++) {
    executor.submit(() -> {
        Thread.sleep(100); // 虚拟线程等待时,平台线程会被释放
        return null;
    });
}

效果:10 万任务几乎同时启动,无需排队,因为虚拟线程不占用宝贵的平台线程资源,I/O 等待时会 “让出” 资源给其他任务。
通俗理解

  • 平台线程 = 一辆满载的卡车,跑起来费油(资源),一次只能拉少量货物(任务)。
  • 虚拟线程 = 自行车队,轻便灵活, thousands of 辆自行车可以共享一条马路(平台线程),I/O 等待时还会主动下车让道。

二、结构化并发(Structured Concurrency):让多任务协作 “有条理”

传统并发的痛点:多任务 “各自为政”,容易出乱子

当一个任务需要拆分多个子任务并行执行时(比如接口需要同时调用 3 个微服务,再汇总结果),传统写法会有两个问题:
1.子任务失败了,其他子任务还在瞎跑,浪费资源;
2.需要手动处理异常和线程关闭,代码臃肿。

比如下面的代码,假设 fetchUser() 失败,fetchOrder() 和 fetchCart() 还会继续执行:

// 传统方式:三个子任务并行执行
ExecutorService executor = Executors.newFixedThreadPool(3);

Future<User> userFuture = executor.submit(() -> fetchUser());
Future<Order> orderFuture = executor.submit(() -> fetchOrder());
Future<Cart> cartFuture = executor.submit(() -> fetchCart());

try {
    User user = userFuture.get(); // 等待用户信息
    Order order = orderFuture.get(); // 等待订单信息
    Cart cart = cartFuture.get(); // 等待购物车信息
    return new Result(user, order, cart);
} catch (Exception e) {
    // 手动取消所有子任务(容易遗漏)
    userFuture.cancel(true);
    orderFuture.cancel(true);
    cartFuture.cancel(true);
    throw e;
}

结构化并发的解决方案:子任务 “荣辱与共”
结构化并发将多个子任务视为一个 “整体”,用 StructuredTaskScope 管理:

  • 父任务等待所有子任务完成;
  • 任何一个子任务失败,自动取消其他子任务;
  • 无需手动关闭线程,自动释放资源。

案例:子任务失败自动取消其他任务

// 结构化并发:子任务绑定到一个“作用域”
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
    // 启动三个子任务(fork = 分叉执行)
    Future<User> userFuture = scope.fork(() -> fetchUser());
    Future<Order> orderFuture = scope.fork(() -> fetchOrder());
    Future<Cart> cartFuture = scope.fork(() -> fetchCart());

    scope.join(); // 等待所有子任务完成
    scope.throwIfFailed(); // 若有子任务失败,直接抛出异常

    // 所有任务成功,汇总结果
    return new Result(
        userFuture.result(),
        orderFuture.result(),
        cartFuture.result()
    );
}

效果:如果 fetchUser() 抛出异常,scope.join() 会发现失败,自动调用 shutdown() 取消 fetchOrder() 和 fetchCart(),避免资源浪费。

通俗理解

  • 传统并发 = 三个朋友各自去买奶茶,其中一个人没买到,另外两个人还在傻傻排队。
  • 结构化并发 = 三个人组队买奶茶,队长(父任务)说:“有人没买到,大家都别买了,赶紧回来!”

三、虚拟线程 + 结构化并发:强强联合

案例:高并发接口同时调用多个服务

// 处理用户请求的接口
public Result handleRequest() throws Exception {
    // 1. 创建虚拟线程的结构化作用域
    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
        // 2. 用虚拟线程启动子任务(默认使用虚拟线程)
        Future<User> userFuture = scope.fork(() -> fetchUser()); // 调用用户服务
        Future<Order> orderFuture = scope.fork(() -> fetchOrder()); // 调用订单服务
        Future<Cart> cartFuture = scope.fork(() -> fetchCart()); // 调用购物车服务

        // 3. 等待所有子任务完成,失败则取消其他任务
        scope.join();
        scope.throwIfFailed();

        // 4. 汇总结果
        return new Result(
            userFuture.result(),
            orderFuture.result(),
            cartFuture.result()
        );
    }
}

这个接口能轻松应对数万并发请求,因为:

  • 虚拟线程让每个子任务的创建成本几乎可以忽略;
  • 结构化并发确保任何子任务失败时,其他任务及时停止,不浪费资源。

总结

JDK 21 的并发模型升级,本质是让开发者 “少关心线程,多关注业务”:

  • 虚拟线程:解决 “线程不够用” 的问题,让高并发场景不再需要复杂的线程池调优;
  • 结构化并发:解决 “多任务协作乱” 的问题,让并发代码像同步代码一样直观。

对于日常开发,优先在 I/O 密集型场景(如 Web 接口、微服务调用)中使用这两个特性,能显著提升系统吞吐量并简化代码。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值