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

相关文章:

  • [无需 Mac] 使用 GitHub Actions 构建 iOS 应用
  • 全栈:如何操作在SQLserver里面CRUD(增删改查)
  • stm32cubeide memory analyzer 不显示BUG
  • 使用Puppeteer轻松自动化浏览器操作
  • 高并发内存池 设计PageCache(4)
  • W25Q64模块
  • jetson上使用opencv的gstreamer进行MIPI和USB摄像头的连接以及udp推流
  • 网站IP被劫持?三步自建防护盾
  • 【基础知识】springboot+vue 基础框架搭建(更新中)
  • 数据库入门:从零开始构建你的第一个数据库
  • [Oracle] DECODE()函数
  • oracle 11G安装大概率遇到问题
  • Java面试宝典:JVM的垃圾收集算法
  • c++之 栈浅析
  • 如何提高云手机中数据信息的安全性?
  • 数字取证:可以恢复手机上被覆盖的数据吗?
  • PostgreSQL 数据库 设置90天密码过期时间的完整方案
  • 数据结构(五):顺序循环队列与哈希表
  • 僵尸进程问题排查
  • 安卓10.0系统修改定制化____修改系统固件 实现刷写完成 开机默认关闭桌面搜索框
  • 【网络编程】IO多路转接——select
  • 2025年我国半导体材料产业链全景分析
  • MySQL聚簇索引与非聚簇索引详解
  • uni-app X能成为下一个Flutter吗?
  • Linux基础测试
  • .NET 10 新增功能系列文章5——C# 14 中的新增功能
  • 人工智能的20大应用
  • 基于django的非物质文化遗产可视化网站设计与实现
  • [LVGL] 布局系统 lv_flex, lv_grid | 输入设备 lv_indev | union
  • 云原生安全挑战与治理策略:从架构思维到落地实践