
spring 中通过 @autowired 注入的 bean 默认是单例(singleton)作用域,其引用存储在栈中,对象实例位于堆中;线程安全与否取决于 bean 自身是否包含可变共享状态,而非 di 框架本身。
spring 中通过 @autowired 注入的 bean 默认是单例(singleton)作用域,其引用存储在栈中,对象实例位于堆中;线程安全与否取决于 bean 自身是否包含可变共享状态,而非 di 框架本身。
在 Spring 应用中,依赖注入(DI)容器(如 ApplicationContext)负责管理 Bean 的生命周期与作用域。默认情况下,所有由 Spring 管理的 Bean 都是 单例(Singleton) —— 即整个应用上下文中仅存在一个实例。该实例在容器启动时创建,并被缓存在堆内存中;而各线程中通过 @Autowired 注入的字段(如 MyClass myClass;),本质上是栈上持有的对该堆中同一对象的引用。
✅ 正确理解关键点:
- 引用(Reference)在线程栈中:每个线程调用方法时,局部变量(包括注入的引用)存于各自栈帧,互不干扰;
- 对象(Object)在共享堆中:单例 Bean 实例驻留在堆内存,被所有线程共同访问;
- DI 框架不保证业务逻辑线程安全:Spring 负责安全地提供和复用 Bean 实例,但不自动同步或隔离对 Bean 内部状态的并发访问。
因此,线程安全性完全取决于 Bean 的实现方式:
? 无状态(Stateless)Bean 是天然线程安全的
@Service
public class MathService {
// ✅ 无任何成员变量 → 完全线程安全
public int add(int a, int b) {
return a + b;
}
}? 有可变共享状态(Mutable Shared State)则需显式同步
@Service
public class CounterService {
private int count = 0; // ❌ 共享可变状态 → 非线程安全
public int increment() {
return ++count; // 竞态条件风险!
}
}修复方案示例(推荐优先级从高到低):
- ✅ 移除可变状态(最简洁可靠):改为方法参数传递,或使用 ThreadLocal 存储线程私有数据;
- ✅ 使用不可变对象/原子类:
private final AtomicInteger count = new AtomicInteger(0); public int increment() { return count.incrementAndGet(); } - ⚠️ 加锁(synchronized / ReentrantLock):适用于复杂逻辑,但可能影响吞吐量;
- ? 避免使用 synchronized 方法修饰符直接修饰整个服务方法:易造成串行瓶颈,应精细化锁粒度。
? 注意事项总结:
- 不要误以为“Spring 注入 = 自动线程安全”——框架只解决对象创建与装配,不介入业务并发控制;
- 若必须维护状态,优先考虑将状态外置(如数据库、Redis)或绑定到请求/线程生命周期(如
@Scope("prototype")或@Scope("request")); - 在 Web 环境中,
@RequestScopeBean 每次 HTTP 请求新建实例,天然隔离,适合含临时状态的场景; - 使用
@Scope("prototype")时需注意:每次注入都新建对象,但若该原型 Bean 又依赖单例 Bean,仍需确保后者线程安全。
归根结底:线程安全不是 DI 的责任,而是开发者对共享资源访问契约的设计结果。 理解栈与堆的分工,能帮你更清醒地判断——谁该负责隔离,谁该负责保护。
















