为何Spring DefaultLifecycleProcessor使用CountDownLatch?多线程场景存疑
关于Spring中CountDownLatch在SmartLifecycle停止逻辑里的作用及疑问解答
核心代码片段
顶层stop方法
public void stop() { ..... CountDownLatch latch = new CountDownLatch(this.smartMemberCount); ..... try { latch.await(this.timeout, TimeUnit.MILLISECONDS); ....... }
doStop方法及说明
停止指定的Lifecycle Bean,确保所有依赖它的Bean先停止。
@param lifecycleBeans 键为Bean名称、值为Lifecycle实例的Map
@param beanName 要停止的Bean名称
private void doStop(Map<String, ? extends Lifecycle> lifecycleBeans, final String beanName, final CountDownLatch latch, final Set<String> countDownBeanNames) { ...... String[] dependentBeans = getBeanFactory().getDependentBeans(beanName); for (String dependentBean : dependentBeans) { doStop(lifecycleBeans, dependentBean, latch, countDownBeanNames); } ...... ((SmartLifecycle) bean).stop(() -> { latch.countDown(); countDownBeanNames.remove(beanName); ...... } }
自定义SmartLifecycle Bean示例
@Component public class Stop1 implements SmartLifecycle { ..... @Override public void stop() { System.err.println("0"); } ..... public int getPhase() { return 1; } } @Component public class Stop2 implements SmartLifecycle { .... @Override public void stop() { try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.err.println("2"); } ...... public int getPhase() { return 1; } } @DependsOn("stop2") public class Stop3 implements SmartLifecycle { ...... @Override public void stop() { System.err.println("3"); } ..... public int getPhase() { return 0; } }
问题解答
1. Spring为什么要用CountDownLatch?
核心是处理异步停止逻辑的等待问题:
SmartLifecycle的stop(Runnable callback)支持异步停止,回调会在Bean停止完成后才执行。如果不用CountDownLatch,主线程无法感知所有SmartLifecycle Bean的异步停止操作何时全部完成,可能导致Bean还没停干净,Spring就继续执行后续关闭流程,引发资源泄漏、数据不一致等问题。
CountDownLatch让主线程在latch.await()处阻塞,直到所有SmartLifecycle Bean完成停止并调用countDown(),计数器归0后才继续执行,保证所有该停的Bean都处理完毕。另外,CountDownLatch本身是线程安全的,多线程场景下无需额外加锁处理计数器的并发修改,避免了线程安全问题。
2. 多线程场景下会不会出现CountDownLatch提前归0、Spring提前关闭的情况?
不会,有两点关键保障:
- 首先,
doStop是递归调用的,会先处理当前Bean的所有依赖Bean的停止逻辑。比如你的例子里Stop3依赖Stop2,Spring会先递归调用doStop处理Stop2,再处理Stop3,确保依赖Bean的停止逻辑先被触发。 - 其次,
countDown()是在Bean停止完成的回调中执行的,不是调用stop()方法就立刻执行。比如Stop3的stop()方法里的逻辑全部执行完毕后,才会触发传入的Runnable执行latch.countDown()。也就是说,计数器减1的动作严格对应“Bean停止完成”这个节点。
哪怕是多线程场景,计数器归0时一定是所有SmartLifecycle Bean都已停止完毕,Spring不会提前关闭,CountDownLatch不会失效。
内容的提问来源于stack exchange,提问作者user16261266
相关产品推荐
相关产品推荐

