为何SpringApplicationBuilder.run要实现线程安全?其设计动机是什么?
疑问背景
我了解Spring Boot的SpringApplicationBuilder.run()方法采用了双重检查机制实现线程安全,但有个疑问:即便该类的其他方法并非线程安全,run方法有必要实现线程安全吗?想明确这段代码的设计动机。
核心代码
public ConfigurableApplicationContext run(String... args) { if (this.running.get()) { // If already created we just return the existing context return this.context; } configureAsChildIfNecessary(args); if (this.running.compareAndSet(false, true)) { synchronized (this.running) { // If not already running copy the sources over and then run. this.context = build().run(args); } } return this.context; }
设计动机解析
杜绝重复创建核心容器:ApplicationContext是Spring应用的核心,创建过程要完成Bean扫描、初始化、依赖注入等大量重操作,成本极高。如果多线程同时调用run,会生成多个独立的容器,不仅浪费系统资源,还会导致Bean多实例、配置逻辑冲突等问题。这段代码通过CAS+双重检查+synchronized的组合,确保只有第一个触发的线程会执行容器创建逻辑,后续线程直接复用已生成的context。
防御性兼容边缘场景:虽然SpringApplicationBuilder的设计初衷是单线程链式调用(比如主线程里先调用各种配置方法,最后调用run),但实际开发中可能出现误操作——比如异步代码里不小心复用了同一个builder实例调用run。给run方法加线程安全保障,能避免这种场景下的崩溃问题,属于典型的防御性编程。
匹配Spring容器的单例语义:Spring应用的ApplicationContext默认是单例语义(一个应用对应一个核心容器),run作为容器创建的入口,必须守住这个语义。而builder的其他非线程安全方法都是构建阶段的配置方法,这些方法的正确用法是在run之前完成调用,多线程调用这些方法本身就不符合构建器模式的使用规范,所以不需要为它们做线程安全。
内容的提问来源于stack exchange,提问作者Droopy

