Spring框架中的单例bean是线程安全的吗?
不是线程安全的,当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线程会并发执行该请求对应的业务逻辑(成员方法),如果该处理逻辑中有对该单列状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题。
Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。
比如:我们通常在项目中使用的Spring bean
都是不可变的状态(比如Service类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。
如果bean有多种状态的话(比如View Model
对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用由singleton
变更为prototype
或者进行加锁
。
线程安全的三大特性
- 原子性
原子性是指操作是不可分的。其表现在于对于共享变量的某些操作,应该是不可分的,必须连续完成。可以使用synchronized
或者是lock
进行加锁处理。 - 可见性
可见性是指一个线程对共享变量的修改,另外一个线程能够立刻看到。
Java方面提供了两个关键字来保证多线程情况下共享变量的可见性方案(volatile
、synchronized
)。 - 有序性
有序性是指程序在执行的时候,程序的代码执行顺序和语句的顺序是一致的。
在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序
,重排序不会影响单线程的执行结果,但是在并发情况下,可能会出现诡异的BUG。
Java中可以使用volatile
来保证顺序性,synchronized
和lock
也可以来保证有序性,和保证原子性的方式一样,通过同一段时间只能一个线程访问来实现的。
JVM 还通过happen-before
原则来隐式的保证顺序性。其中有一条就是适用于volatile
关键字的,针对于volatile关键字的写操作肯定是在读操作之前,也就是说读取的值肯定是最新的。
操作系统通过总线lock锁
和MESI(缓存一致性协议)
来实现内存可见性。