文章目录
Spring中单例Bean会存在线程安全吗?
分情况分状态讨论:
- 创建:spring容器会保证Bean创建过程中的线程安全。
- 使用:
- 无状态的Bean是线程安全的。特点:
- 没有成员变量或者不被修改。
- 方法只使用局部变量
- 有状态的Bean。
- 存在成员变量并且存在修改
- 修改函数未加锁或其它同步操作。
- 无状态的Bean是线程安全的。特点:
如何保证单例Bean线程安全
出现线程安全的原因本质是:多个线程同时并发读写同一份可变共享数据,且没有任何同步控制。
- 保证创建的Bean是无状态的。
只读操作是线程安全 - 使用ThreadLocal每个线程操作单独的副本或者将
Singleton转换为Prototype。将共享转换为不共享 - 加锁或者使用原子类/线程安全类。
进行同步控制
什么是循环依赖?Spring可以解决哪些类型的循环依赖
循环依赖:多个对象之间相互依赖,形成一个闭环。例如A->B->A,以下一个例子为例
@Component
class A {
@Autowired
private B b;
}
@Component
class B {
@Autowired
private A a;
}
可以自行解决的循环依赖类型
- A/B的对应属性采用setter方法注入
- A/B对应属性采用属性自动注入
不可以解决的循环依赖的类型: - AB为多例Bean
- A/B对应属性都采用构造器注入
- A/B对应属性存在构造器注入(例如A类采用构造器注入,B中采用属性/setter方法注入)
Spring是如何解决循环依赖的
Spring借助三级缓存机制解决循环依赖。spring中定义了三个缓存
singletonObjects: 一级缓存用于存放完全初始化好的BeanearlySingletonObjects: 二级缓存用于存放提前暴露的beansingletonFactories:三级缓存用于存放Bean工厂。
解决三级缓存解决缓存依赖的流程,以A/B类为例:
- 实例化A,此时A为空壳对象。放入三级缓存
singletonFactories - A进行输入注入,发现依赖B对象。调用
getSingleton('B')获取,此时B还未创建,会实例化B。 - 实例化B,B也放入三级缓存中。此时发现B需要A,调用
getSingleton('A')获取,会命中三级缓存返回A的早期对象。将A早期对象放入二级缓存earlySingletonObjects删除三级缓存中的 A. - B获取到A早期对象之后,完成完整创建。放入一级缓存。此时回到A
- A获取到一级缓存中的B,A初始化完成。A也放入一级缓存。