1、Spring单例模式及线程安全
Spring框架中的Bean,或者说组件,获取实例的时候都是默认单例模式,这是在多线程开发的时候须要尤为注意的地方。安全
单例模式的意思是只有一个实例,例如在Spring容器中某一个类只有一个实例,并且自行实例化后并项整个系统提供这个实例,这个类称为单例类。多线程
当多个用户同时请求一个服务时,容器会给每个请求分配一个线程,这时多个线程会并发执行该请求对应的业务逻辑(成员方法),此时就要注意了,若是该处理逻辑中有对单例状态的修改(体现为该单例的成员属性),则必须考虑线程同步问题。并发
同步机制的比较:框架
ThreadLocal和线程同步机制相比有什么优点呢?他们都是为了解决多线程中相同变量的访问冲突问题。less
在同步机制中,经过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析何时对变量进行读写,何时须要锁定某个对象,何时释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
而ThreadLocal则从另外一个角度来解决多线程的并发访问。ThreadLocal会为每个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。由于每个线程都拥有本身的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,能够把不安全的变量封装进ThreadLocal。
因为ThreadLocal中能够持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,须要强制类型转换。但JDK 5.0经过泛型很好的解决了这个问题,在必定程度地简化ThreadLocal的使用
归纳起来讲,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不一样的线程排队访问,然后者为每个线程都提供了一份变量,所以能够同时访问而互不影响。
Spring使用ThreadLocal解决线程安全问题
咱们知道在通常状况下,只有无状态的Bean才能够在多线程环境下共享,在Spring中,绝大部分Bean均可以声明为singleton做用域。就是由于Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,由于有状态的Bean就能够在多线程中共享了。
通常的Web应用划分为展示层、服务层和持久层三个层次,在不一样的层中编写对应的逻辑,下层经过接口向上层开放功能调用。在通常状况下,从接收请求到返回响应所通过的全部程序调用都同属于一个线程
ThreadLocal是解决线程安全问题一个很好的思路,它经过为每一个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在不少状况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
若是你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。若是每次运行结果和单线程运行的结果是同样的,并且其余的变量的值也和预期的是同样的,就是线程安全的。 或者说:一个类或者程序所提供的接口对于线程来讲是原子操做或者多个线程之间的切换不会致使该接口的执行结果存在二义性,也就是说咱们不用考虑同步的问题。 线程安全问题都是由全局变量及静态变量引发的。
若每一个线程中对全局变量、静态变量只有读操做,而无写操做,通常来讲,这个全局变量是线程安全的;如有多个线程同时执行写操做,通常都须要考虑线程同步,不然就可能影响线程安全。
1) 常量始终是线程安全的,由于只存在读操做。
2)每次调用方法前都新建一个实例是线程安全的,由于不会访问共享的资源。
3)局部变量是线程安全的。由于每执行一个方法,都会在独立的空间建立局部变量,它不是共享的资源。局部变量包括方法的参数变量和方法内变量。
有状态就是有数据存储功能。有状态对象(Stateful Bean),就是有实例变量的对象 ,能够保存数据,是非线程安全的。在不一样方法调用间不保留任何状态。
无状态就是一次操做,不能保存数据。无状态对象(Stateless Bean),就是没有实例变量的对象 .不能保存数据,是不变类,是线程安全的。
有状态对象:
无状态的Bean适合用不变模式,技术就是单例模式,这样能够共享实例,提升性能。有状态的Bean,多线程环境下不安全,那么适合用Prototype原型模式。Prototype: 每次对bean的请求都会建立一个新的bean实例。
Struts2默认的实现是Prototype模式。也就是每一个请求都新生成一个Action实例,因此不存在线程安全问题。须要注意的是,若是由Spring管理action的生命周期, scope要配成prototype做用域
2、线程安全案例
SimpleDateFormat(下面简称sdf)类内部有一个Calendar对象引用,它用来储存和这个sdf相关的日期信息,例如sdf.parse(dateStr), sdf.format(date) 诸如此类的方法参数传入的日期相关String, Date等等, 都是交友Calendar引用来储存的.这样就会致使一个问题,若是你的sdf是个static的, 那么多个thread 之间就会共享这个sdf, 同时也是共享这个Calendar引用,非线程安全的。性能
这个问题背后隐藏着一个更为重要的问题--无状态:无状态方法的好处之一,就是它在各类环境下,均可以安全的调用。衡量一个方法是不是有状态的,就看它是否改动了其它的东西,好比全局变量,好比实例的字段。format方法在运行过程当中改动了SimpleDateFormat的calendar字段,因此,它是有状态的。
spa
这也同时提醒咱们在开发和设计系统的时候注意下一下三点:
1.本身写公用类的时候,要对多线程调用状况下的后果在注释里进行明确说明
2.对线程环境下,对每个共享的可变变量都要注意其线程安全性
3.咱们的类和方法在作设计的时候,要尽可能设计成无状态的
解决方法:
prototype
1.使用synchronized关键字进行数据同步,或者使用ThreadLocal保证线程安全线程
2.不适用JDK自带的时间格式化类,使用其余类库的设计
- 使用Apache commons里的FastDateFormat,宣城是既快有线程安全的SimpleDateFormat,惋惜他只能对日期进行format,不能对日期串进行解析
- 使用Joda-Time类库来处理时间相关问题,该种对时间的处理方式比较完美,建议使用。