多线程 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
。
注意点
-
阻塞的是调用
get()
的线程:
只有调用get()
的线程(如上例的主线程)会阻塞,其他子线程仍可并行执行。 -
可以设置超时时间:
若不想无限等待,可使用future.get(long timeout, TimeUnit unit)
设置超时,超时后会抛出TimeoutException
:try {future.get(3, TimeUnit.SECONDS); // 最多等3秒 } catch (TimeoutException e) {// 超时处理(如终止子线程) }
-
异常会被包装:
若子线程执行中抛出异常,get()
会将异常包装成ExecutionException
抛出,需通过e.getCause()
获取原始异常。
总结:future.get()
的阻塞是让主线程“停下来等子线程完成”的关键机制,确保主线程在所有子线程处理完毕后,再执行后续的汇总、保存等操作。