欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring的线程安全

程序员文章站 2022-06-05 16:07:06
Spring作为一个IOC/DI容器,帮助我们管理了许许多多的“bean”。但其实,Spring并没有保证这些对象的线程安全,需要由开发者自己编写解决线程安全问题的代码。Spring对每个bean提供了一个scope属性来表示该bean的作用域。它是bean的生命周期。例如,一个scope为sing ......

Spring作为一个IOC/DI容器,帮助我们管理了许许多多的“bean”。但其实,Spring并没有保证这些对象的线程安全,需要由开发者自己编写解决线程安全问题的代码。
Spring对每个bean提供了一个scope属性来表示该bean的作用域。它是bean的生命周期。例如,一个scope为singleton的bean,在第一次被注入时,会创建为一个单例对象,该对象会一直被复用到应用结束

singleton:默认的scope,每个scope为singleton的bean都会被定义为一个单例对象,该对象的生命周期是与Spring IOC容器一致的(但在第一次被注入时才会创建)。
prototype:bean被定义为在每次注入时都会创建一个新的对象。

request:bean被定义为在每个HTTP请求中创建一个单例对象,也就是说在单个请求中都会复用这一个单例对象。
session:bean被定义为在一个session的生命周期内创建一个单例对象。
application:bean被定义为在ServletContext的生命周期中复用一个单例对象。
websocket:bean被定义为在websocket的生命周期中复用一个单例对象。

 

无状态对象和有状态对象:

有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象,可以保存数据,是非线程安全的。在不同方法调用间不保留任何状态。 
无状态就是一次操作,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象.不能保存数据,是不变类,不会因为多个线程的交替调度而破坏自身状态导致线程安全问题。 

1.相信大家已经对有状态和无状态有了一定的理解。无状态的Bean适合用单例模式,这样可以共享实例,提高性能。有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。Prototype: 每次对bean的请求都会创建一个新的bean实例。 

2.默认情况下,从Spring bean工厂所取得的实例为singleton(scope属性为singleton),容器只存在一个共享的bean实例。 

3.理解了两者的关系,那么scope选择的原则就很容易了:有状态的bean都使用prototype作用域,而对无状态的bean则应该使用singleton作用域。 

4.如Service层、Dao层用默认singleton就行,虽然Service类也有dao这样的属性,但dao这些类都是没有状态信息的,也就是相当于不变(immutable)类,所以不影响。Struts2中的Action因为会有User、BizEntity这样的实例对象,是有状态信息的,在多线程环境下是不安全的,所以Struts2默认的实现是Prototype模式。在Spring中,Struts2的Action中,scope要配成prototype作用域。

我们交由Spring管理的大多数对象其实都是一些无状态的对象,这种不会因为多线程而导致状态被破坏的对象很适合Spring的默认scope(singleton),每个单例的无状态对象都是线程安全的(也可以说只要是无状态的对象,不管单例多例都是线程安全的,不过单例毕竟节省了不断创建对象与GC的开销)

有人可能会认为,我使用request作用域不就可以避免每个请求之间的安全问题了吗?这是完全错误的,因为Controller默认是单例的,一个HTTP请求是会被多个线程执行的,这就又回到了线程的安全问题。当然,你也可以把Controller的scope改成prototype,实际上Struts2就是这么做的,但有一点要注意,Spring MVC对请求的拦截粒度是基于每个方法的,而Struts2是基于每个类的,所以把Controller设为多例将会频繁的创建与回收对象,严重影响到了性能。

Spring根本就没有对bean的多线程安全问题做出任何保证与措施。对于每个bean的线程安全问题,根本原因是每个bean自身的设计,不要在bean中声明任何有状态的实例变量或类变量。

解决方案:

有几种解决方法:
1、不要在bean中声明任何有状态的实例变量或类变量

2、如果必须如此,那么就使用ThreadLocal把变量变为线程私有的

3、如果bean的实例变量或类变量需要在多个线程之间共享,那么就只能使用synchronized、lock、CAS等这些实现线程同步的方法了

4、在spring配置文件Controller中声明 scope="prototype",每次都创建新的controller