当前位置: 首页 > 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.lryc.cn/news/613471.html

相关文章:

  • tcpdump问题记录
  • 【多重BFS】Monsters
  • 【实时Linux实战系列】基于实时Linux的高频交易系统构建
  • 【C语言】深入理解编译与链接过程
  • 数据标注之数据集的类型与如何标注
  • 时间并非维度:论其作为空间变化的转换系数
  • 大模型LL04 微调prompt-Tuning方法入门(背景与发展)
  • 深度学习的视觉惯性里程计(VIO)算法优化实践
  • 数据结构学习之二叉树
  • 深度学习(2):自动微分
  • LSTM 单变量时序预测—pytorch
  • JAVA第六学:数组的使用
  • 【数据结构】二叉树练习
  • S7-1200 串行通信介绍
  • 一场 Dark Theme A/B 测试的复盘与提效实践
  • Linux上MySql CPU 占用异常
  • SpringBoot中的单例注入方式
  • windows有一个企业微信安装包,脚本执行并安装到d盘。
  • VSCode ssh一直在Setting up SSH Host xxx: Copying VS Code Server to host with scp等待
  • 开发避坑指南(20) :MyBatis操作Oracle插入NULL值异常“无效列类型1111“解决方案
  • DrissionPage实战案例:小红书旅游数据爬取
  • TDengine IDMP 文档介绍
  • 腾讯位置服务 —— 预估订单路线金额(使用Drools规则引擎处理)
  • 机器学习在量化中的应用:如何从逻辑回归到XGBoost实现高效预测?
  • [Oracle] DECODE()函数
  • DBeaver 25.1.0 转储数据库失败解决方案(适配最新版界面)
  • [Oracle] GREATEST()函数
  • 数据库入门:从零开始构建你的第一个数据库
  • 一个基于固定 IP地址查询天气的 C 语言程序,通过调用第三方天气 API:
  • Oracle 关闭 impdp任务