同步多异步请求后关闭服务:哪种实现方案最优?
先明确核心需求:我们的Service是异步实现的,需要在所有请求(无论成功或失败)都执行完成后安全关闭它。下面逐个拆解三个方案的优劣:
方案1:回调内判断CountDownLatch计数后关闭
这个思路是在每个请求的回调里调用countDown(),然后检查计数是否归0,归0就触发Service关闭。但这里存在一个明显的隐患:当最后几个请求同时完成时,多个线程可能同时进入if (latch.getCount() == 0)的判断逻辑,导致svc.close()被多次调用。虽然规范的Closeable实现应该保证close()是幂等的,但这毕竟不是预期行为,万一遇到非幂等的close实现,就可能引发未知问题。
方案2:主线程等待CountDownLatch后关闭
这个方案的逻辑最清晰:主线程发起所有请求后,调用latch.await()阻塞等待,直到所有请求都完成(回调里完成countDown()),然后再统一调用一次svc.close()。
它的优势非常突出:
- 彻底避免了方案1的重复调用问题,确保
close()只会被执行一次 - 代码逻辑直观易懂,后续维护成本极低
需要注意的是,latch.await()会抛出InterruptedException,实际业务代码里需要处理这个异常(捕获或者向上抛出),示例里的代码漏掉了这一点,改进后的参考代码如下:
public void processRequests(ArrayList<Integer> params) throws InterruptedException { Service svc = new Service(); CountDownLatch latch = new CountDownLatch(params.size()); for (Integer param : params) { final ListenableFuture<Integer> res = svc.processRequest(param); Futures.addCallback(res, new FutureCallback<Integer>() { @Override public void onSuccess(Integer integer) { latch.countDown(); } @Override public void onFailure(Throwable throwable) { latch.countDown(); // 这里可以额外添加失败日志等处理,不影响核心关闭逻辑 } }); } latch.await(); // 阻塞等待所有请求执行完成 svc.close(); }
如果你的场景不允许主线程阻塞(比如Web容器环境),可以把等待和关闭的逻辑放到一个单独的后台线程里,但核心思路还是让一个线程专门负责等待所有请求完成后执行一次关闭操作。
方案3:用AtomicInteger替代CountDownLatch
这个方案本质和方案1完全一致:用原子变量计数,每个回调里将计数减1,然后判断是否归0就触发关闭。同样会遇到多个线程同时触发close的问题,和方案1的隐患完全相同,并没有本质改进。
总结:最佳选择是方案2
方案2是最可靠、最易维护的实现方式,它完美贴合“所有请求完成后关闭”的需求,同时保证了关闭操作的唯一性,逻辑也最符合直觉。
内容的提问来源于stack exchange,提问作者Hanna Khalil

