future.get(timeout)不自动重试,需将timeoutexception和interruptedexception作为控制信号触发重试;必须在每次重试前调用future.cancel(true)终止原任务,避免资源堆积与非幂等风险;正确处理中断状态,指数退避加随机抖动提升稳定性。

Future.get(timeout) 本身不重试,但你可以用它触发重试;关键不是等超时,而是把 TimeoutException 和 InterruptedException 当作控制信号来决策是否重试。
为什么不能直接在 get() 外套 while 循环重试
很多初学者写成这样:
while (retryCount <p>问题在于:<code>future</code> 还在后台跑着,没被取消,重复提交会堆积任务、耗尽线程池资源。更糟的是,如果原任务是发 HTTP 请求或写数据库,可能已成功执行,只是响应慢——重试就变成非幂等操作。</p>
- 每次重试前必须调用
future.cancel(true),否则原任务持续占用线程 -
cancel(true)仅对未开始或正在运行的任务生效;若任务已结束(哪怕抛异常),cancel 无效果 - 不要假设
TimeoutException意味着“服务没收到请求”,它只说明“你没等到结果”
如何正确响应 InterruptedException 并保持重试可控
InterruptedException 不是失败信号,而是协作式中断协议的一部分。你在重试循环里主动中断自己(比如超时后调用 Thread.currentThread().interrupt()),或外部线程中断了当前等待线程,都得处理干净,否则中断状态丢失,上层无法感知。
- 捕获
InterruptedException后,必须立即调用Thread.currentThread().interrupt()恢复中断标志 - 不要在重试循环内吞掉
InterruptedException并继续下一轮——这会让调用方误以为操作还在进行 - 如果重试逻辑本身被中断(例如 Spring 的
@Async方法被Future.cancel(true)),应直接退出,不再发起新任务
带退避的重试模板:兼顾 TimeoutException 和 InterruptedException
下面是一个生产可用的轻量级重试结构,不依赖第三方库:
public <t> T getWithRetry(Future<t> future, long baseDelayMs, int maxRetries)
throws ExecutionException, InterruptedException {
for (int i = 0; i <p>注意点:</p>
<ul>
<li>指数退避用 <code>1L 替代 <code>Math.pow(2, i)</code>,避免浮点误差和装箱开销</code>
</li>
<li>每次重试前 sleep 时间包含随机抖动(<code>jitter</code>),防止重试洪峰打垮下游</li>
<li>最后一轮失败时,不捕获 <code>TimeoutException</code>,让它向上冒泡,由调用方决定兜底(如返回默认值或降级)</li>
</ul>
<h3>最容易被忽略的边界:CancellationException 和已完成任务的重试判断</h3>
<p>当 <code>future.cancel(true)</code> 成功后,再调用 <code>get()</code> 会抛 <code>CancellationException</code>,但它和 <code>TimeoutException</code> 不同——前者代表“你主动杀了它”,后者只是“它还没回你”。如果你没显式 catch <code>CancellationException</code>,它会混在 <code>ExecutionException</code> 里被忽略。</p>
<ul>
<li>重试前先检查 <code>future.isDone()</code>:如果是 <code>true</code>,说明任务其实已完成(成功或异常),不该重试</li>
<li>若 <code>future.isCancelled()</code> 为 <code>true</code>,说明你刚 cancel 掉它,此时可安全新建任务</li>
<li>永远不要对已 <code>isDone()</code> 的 <code>future</code> 调用 <code>get()</code>——它可能抛 <code>ExecutionException</code>(含原始异常),也可能直接返回结果,行为不一致</li>
</ul></t></t>










