Spring创建单例Bean的过程本身线程安全,无需开发者加synchronized;synchronized用于保护已创建Bean在多线程调用其方法时对可变状态的并发访问。

Spring 容器在创建单例 Bean 时本身是线程安全的,synchronized 并不用于 Bean 的创建过程,而是用于 Bean 创建完成之后、被多个线程并发调用其方法时的运行时状态访问控制。
换句话说:synchronized 不是用来保护“Spring 怎么 new 出这个 Bean”的,而是用来保护“这个已经创建好的 Bean 实例,它的某些方法或字段被多线程同时修改时不出错”。
为什么 Bean 创建过程不需要你加 synchronized?
Spring 容器(如 DefaultListableBeanFactory)在初始化单例 Bean 时,内部已通过双重检查锁 + ConcurrentHashMap 缓存 + synchronized 块等方式确保:
- 同一个 Bean 名称只会被初始化一次;
- 即使多个线程并发请求同一个单例 Bean,也只会有一个线程执行初始化逻辑,其余线程阻塞等待或直接获取已初始化好的实例。
所以:Bean 构造、依赖注入、@PostConstruct 执行等初始化流程,Spring 自己就做了线程安全保证。
立即学习“Java免费学习笔记(深入)”;
那 synchronized 用在哪?——解决 Bean 使用阶段的线程安全
当你的单例 Bean 内部持有可变状态(比如计数器、缓存 Map、临时上下文),而这些状态会被多个线程读写时,就需要加同步控制。常见方式:
-
在方法上加
synchronized@Service public class CounterService { private int count = 0; public synchronized void increment() { count++; // 线程安全地修改共享状态 } public synchronized int getCount() { return count; } }✅ 优点:简单直接,JVM 层面保障;
❌ 缺点:整个方法串行执行,吞吐量下降,可能成为瓶颈。 -
在代码块中加
synchronized(this)或指定锁对象private final Object lock = new Object(); public void increment() { synchronized (lock) { count++; } }更灵活,可避免锁粒度太粗。
-
替代方案(非 synchronized):
- 用
AtomicInteger替代int(无锁、高效) - 用
ConcurrentHashMap替代HashMap - 用
ThreadLocal隔离线程状态(适合“每个线程一份副本”的场景) - 改为
@Scope("prototype"),让每次注入都新建实例(适合有状态且不共享的场景)
- 用
一个典型误区澄清
有人会写这样的代码试图“保护 Bean 创建”:
@Service
public class MyService {
private static MyService instance;
private MyService() {}
public static synchronized MyService getInstance() { // ❌ 没用!Spring 不走这个
if (instance == null) {
instance = new MyService();
}
return instance;
}
}这是 GoF 单例模式写法,和 Spring 容器无关。Spring 从不调用 getInstance(),它直接通过反射调用构造器,然后管理生命周期。这类手动单例不仅多余,还可能破坏 Spring 的 AOP、事务、代理等机制。
总结关键点
- Spring 创建单例 Bean 的过程是线程安全的,无需、也不该由开发者用
synchronized干预; -
synchronized是给 Bean 的业务方法加的,防止多个线程并发修改其内部可变状态; - 是否需要加,取决于 Bean 是否有可变成员变量(即是否“有状态”);
- 优先考虑无状态设计;其次选
ThreadLocal或原子类;最后再用synchronized或ReentrantLock。
不复杂但容易忽略:线程安全问题不在“怎么造”,而在“怎么用”。


















