countdown() 不在 finally 中会导致异常时未执行而使 await() 永久阻塞;必须置于 finally 中以确保无论正常、异常、中断或 return 都能执行。

countDown() 不在 finally 中的典型风险
当 countDown() 放在 try 块内部,而任务中途抛出异常又未被捕获或未处理时,该语句根本不会执行。计数器无法减一,await() 就永远等不到归零,主线程被永久挂起。
常见触发场景
以下几种情况都可能导致 countDown() 被跳过:
- 任务中发生未捕获的 RuntimeException(如 NullPointerException、ArrayIndexOutOfBoundsException)
- 调用外部服务时抛出受检异常,但 catch 块里没写 countDown(),也未 re-throw
- 线程被中断(InterruptedException),但只做了 Thread.currentThread().interrupt(),却遗漏了 countDown()
- 任务逻辑包含 return 语句提前退出,且位于 countDown() 之前
为什么 finally 是唯一可靠位置
finally 块的语义保证:无论 try 块正常结束、异常退出,还是被中断、return 退出,它都会执行。把 countDown() 放在这里,就等于给每个任务“强制签收”——不管成不成功,都算完成一次等待项。
注意:不能靠“我代码很稳,不会出错”来规避,因为网络超时、资源不可用、JVM 内部异常等外部因素不可控。
对比验证:错误写法 vs 正确写法
危险写法(无 finally):
try { doWork(); countDown(); } catch (Exception e) { log.error(e); }
→ 异常发生在 doWork() 中间,countDown() 永远不执行。
安全写法(带 finally):
try { doWork(); } catch (Exception e) { log.error(e); } finally { countDown(); }
→ 即使 doWork() 抛 NPE 或 OOM,countDown() 仍会执行。










