Spring本身并没有针对Bean做线程安全的处理,所以:
-
如果Bean是⽆状态的,那么Bean则是线程安全的
-
如果Bean是有状态的,那么Bean则不是线程安全的
另外,Bean是不是线程安全,跟Bean的作⽤域没有关系,Bean的作⽤域只是表示Bean的⽣命周期范 围,对于任何⽣命周期的Bean都是⼀个对象,这个对象是不是线程安全的,还是得看这个Bean对象本身。
Spring 中 Bean 的线程安全性解析
在 Spring 框架中,Bean 的线程安全性取决于其 作用域(Scope) 和 自身实现,不能一概而论。以下是详细分析:
一、Spring Bean 的线程安全性规则
Bean 作用域 | 是否线程安全 | 原因 |
---|---|---|
Singleton(默认) | ❌ 不安全 | 所有线程共享同一个实例,需自行保证状态安全 |
Prototype | ✅ 安全 | 每次请求创建新实例,线程间无共享 |
Request | ✅ 安全 | 每个 HTTP 请求创建独立实例 |
Session | ✅ 安全 | 每个用户会话创建独立实例 |
Application | ❌ 不安全 | 类似 Singleton,整个 ServletContext 共享 |
二、Singleton Bean 的线程安全问题
1. 问题场景
@Service // 默认 Singleton
public class CounterService {
private int count = 0; // 共享状态
public void increment() {
count++; // 非原子操作,线程不安全!
}
}
-
多线程并发调用
increment()
会导致count
结果不可预测。
2. 解决方案
(1) 无状态设计(推荐)
@Service
public class StatelessService {
// 不包含共享字段,线程安全
public String process(String input) {
return input.toUpperCase();
}
}
关键:避免在 Singleton Bean 中使用可变的成员变量。
(2) 使用线程安全类
@Service
public class CounterService {
private final AtomicInteger count = new AtomicInteger(0); // 线程安全计数器
public void increment() {
count.incrementAndGet();
}
}
(3) 同步控制
@Service
public class CounterService {
private int count = 0;
private final Object lock = new Object();
public void increment() {
synchronized (lock) { // 加锁保证原子性
count++;
}
}
}
代价:性能下降,可能引发死锁。
(4) 改为 Prototype 作用域
@Service
@Scope("prototype") // 每次请求新建实例
public class CounterService {
private int count = 0; // 线程安全(因为无共享)
}
缺点:频繁创建对象增加 GC 压力。
三、其他作用域 Bean 的线程安全
1. Prototype Bean
-
每次依赖注入或
getBean()
调用都会生成新实例,天然线程安全。 -
适用场景:有状态的工具类(如购物车、计算器)。
2. Request/Session Bean
六、测试线程安全性
使用多线程测试工具验证:
-
每个 HTTP 请求或会话独占一个实例,线程安全。
-
注意:需在 Web 环境中使用,且注入 Singleton Bean 时需加代理:
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
四、Spring 内置 Bean 的线程安全性
Bean 类型 线程安全 说明 Controller ❌ 不安全 默认 Singleton,需自行保证无状态 Service/Repository ❌ 不安全 同 Controller Configuration ✅ 安全 代理机制保证配置类无状态 Util 工具类 ✅ 安全 如 StringUtils
、DateUtils
无状态
五、最佳实践
-
优先设计无状态 Bean:避免在 Singleton Bean 中使用成员变量。
-
必须共享状态时:
-
使用线程安全类(如
AtomicInteger
、ConcurrentHashMap
)。 -
或改为 Prototype 作用域。
-
-
谨慎使用同步锁:可能成为性能瓶颈。
-
Web 作用域 Bean:确保正确配置代理模式。
六、测试线程安全性
使用多线程测试工具验证:
@Test
public void testSingletonThreadSafety() throws InterruptedException {
CounterService service = context.getBean(CounterService.class);
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.submit(service::increment);
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.SECONDS);
assertThat(service.getCount()).isEqualTo(1000); // 可能失败!
}
总结
-
Spring 不保证 Bean 的线程安全,Singleton Bean 需开发者自行处理并发问题。
-
无状态设计是最佳实践,必须共享状态时选择线程安全方案。
-
作用域选择:无状态用 Singleton,有状态用 Prototype/Request/Session。
一句话:Spring 只管理 Bean 的生命周期,线程安全是开发者的责任!