当前位置: 首页 > news >正文

多线程 future.get()的线程阻塞是什么意思?

List<Future<?>> futures = new ArrayList<>();// 提交子线程任务,并用 Future 跟踪
for (/* 子线程任务 */) {Future<?> future = executor.submit(() -> {finEodBalancePOS.add(new FinCiticFlowPO(...));});futures.add(future);
}// 等待所有子线程执行完毕
for (Future<?> future : futures) {try {future.get(); // 阻塞主线程,直到子线程完成} catch (InterruptedException | ExecutionException e) {// 处理异常(如子线程执行失败)log.error("子线程执行异常", e);}
}// 所有子线程完成后,执行保存
finCiticFlowService.saveBatch(finEodBalancePOS);

背景:多线程的线程阻塞问题:

在多线程中,future.get() 的“线程阻塞”指的是:调用 get() 方法的线程(通常是主线程)会暂停执行,进入等待状态,直到对应的子线程任务执行完毕,才会继续执行 get() 之后的代码。

简单说,就是“谁调用 get(),谁就停下来等”。

举个生活例子理解

  • 你(主线程)让 3 个同事(子线程)各自完成一份报告,然后你需要等所有人都写完报告,才能把报告汇总提交。
  • 你给每个同事发了一个“进度追踪器”(Future 对象)。
  • 当你查看第一个同事的追踪器(future1.get())时,你会站在原地等他写完(阻塞);他写完后,你再查看第二个同事的追踪器(future2.get()),继续等第二个写完;直到所有人都写完,你才开始汇总(执行后续代码)。

代码层面的阻塞表现

// 主线程提交 2 个子线程任务
Future<?> future1 = executor.submit(() -> {System.out.println("子线程1开始执行");Thread.sleep(2000); // 模拟耗时操作System.out.println("子线程1执行完毕");
});Future<?> future2 = executor.submit(() -> {System.out.println("子线程2开始执行");Thread.sleep(1000); // 模拟耗时操作System.out.println("子线程2执行完毕");
});// 主线程调用 future.get()
System.out.println("主线程开始等待子线程1...");
future1.get(); // 主线程阻塞,等待子线程1完成(约2秒)
System.out.println("主线程继续等待子线程2...");
future2.get(); // 此时子线程2可能已完成,主线程几乎不阻塞
System.out.println("所有子线程完成,主线程继续执行");

执行顺序输出

子线程1开始执行
子线程2开始执行
主线程开始等待子线程1...
子线程2执行完毕(1秒后)
子线程1执行完毕(2秒后)
主线程继续等待子线程2...(此时子线程2已完成,无阻塞)
所有子线程完成,主线程继续执行
  • 主线程在 future1.get() 处会停 2 秒(等子线程1完成),期间不会执行后续代码。
  • 子线程2虽然 1 秒就完成了,但主线程仍在等子线程1,所以 future2.get() 要等到主线程从 future1.get() 恢复后才会执行(此时子线程2已完成,所以不阻塞)。

为什么需要阻塞?

future.get() 的阻塞是主动等待子线程结果的机制。在你的业务中(等所有子线程添加完数据再批量保存),必须通过这种阻塞确保:

  • 所有子线程的 add 操作都已执行完毕。
  • List 中包含了完整的所有数据,再执行 saveBatch

注意点

  1. 阻塞的是调用 get() 的线程
    只有调用 get() 的线程(如上例的主线程)会阻塞,其他子线程仍可并行执行。

  2. 可以设置超时时间
    若不想无限等待,可使用 future.get(long timeout, TimeUnit unit) 设置超时,超时后会抛出 TimeoutException

    try {future.get(3, TimeUnit.SECONDS); // 最多等3秒
    } catch (TimeoutException e) {// 超时处理(如终止子线程)
    }
    
  3. 异常会被包装
    若子线程执行中抛出异常,get() 会将异常包装成 ExecutionException 抛出,需通过 e.getCause() 获取原始异常。

总结:future.get() 的阻塞是让主线程“停下来等子线程完成”的关键机制,确保主线程在所有子线程处理完毕后,再执行后续的汇总、保存等操作。

http://www.dtcms.com/a/319410.html

相关文章:

  • Spark Memory 内存设计的核心组件
  • 2025年主流开源音视频播放项目深度解析
  • 数据结构——B-树、B+树、B*树
  • flutter-使用AnimatedDefaultTextStyle实现文本动画
  • 状压DP-子集枚举技巧
  • MySQL UNION 操作符详细说明
  • 机器视觉系统工业相机的成像原理及如何选型
  • 数据结构-哈希表(散列表)
  • 进程控制:进程的创建、终止、阻塞、唤醒、切换等生命周期管理操作
  • 基于深度学习的调制信号分类识别算法的研究生学习之旅
  • C语言sprintf、strcmp、strcpy、strcat函数详解:字符串操作的核心工具
  • Modbus转Profinet网关与西门子PLC的互联配置案例:用于永宏品牌变频器的控制实现
  • 一个基于 epoll 实现的多路复用 TCP 服务器程序,相比 select 和 poll 具有更高的效率
  • 并发编程(三)线程模型和通信
  • 【AI算法承载】海思3516DV500+IMX664方案一体机芯,开放AI算法部署二次开发
  • 蓝桥杯----数码管、按键、定时器与中断
  • PTrade详细介绍
  • 【遥感图像入门】遥感中的“景”是什么意思?
  • 深入理解 ReentrantLock和AQS底层源码
  • 专题:2025财务转型与AI赋能数字化报告|附30+份报告PDF汇总下载
  • 《深入解析缓存三大难题:穿透、雪崩、击穿及应对之道》
  • cv2.threshold cv2.morphologyEx
  • 宝塔面板配置Nacos集群
  • Plant Biotechnol J(IF=10.5)|DAP-seq助力揭示葡萄白粉病抗性机制
  • 什么是POE接口?通俗理解
  • Pytest项目_day07(pytest)
  • MySql MVCC的原理总结
  • S7-1200 串行通信介绍
  • 配送算法9 A GRASP algorithm for the Meal Delivery Routing Problem
  • React 中 useRef 使用方法