线程安全:多个线程访问同一个变量/对象,得到的结果相同,那么是线程安全的。
线程不安全:多个线程访问同一个变量/对象,得到的结果不相同,那么是线程不安全的。
Spring在创建Bean默认是单例模式。所以Controller、Service、Dao等在创建后只会使用原来的实例。相比之下,Struts2不是单例,request来了就实例化一个Controller,因此是线程安全。而SpringMVC这样的好处是减少了每次实例化对象和垃圾回收的开销,提高了效率,正因此存在线程不安全的风险。
但在实际情况下,SpringMVC不存在线程不安全情况,原因是:
- Controller中的成员变量基本上都是spring容器创建的对象(Autowired注入的),也属于单例。
- 方法参数变量以及方法体内的局部变量等,属于线程私有变量,方法执行完成,参数变量内存释放,变量销毁。
这样的实例在创建出来后是不会有数据的改变的,因此是无状态实例,即使多线程访问了,也不会带来线程安全问题。
但如果成员变量里带有不是Autorwired注入的变量则会带来数据错误(https://www.cnblogs.com/lmj612/p/10509556.html),所以我们要尽量避免定义这种变量。实在不行,也可以考虑:1.在Controller中使用ThreadLocal变量,可保证该变量的线程安全;
2.在spring配置文件Controller中声明 scope=“prototype”,即把默认的单例改成多例。
综上,即使是单例,SpringMVC可以保证是线程安全的。
补充:关于单例模式和Spring源码的实现:https://www.cnblogs.com/nickup/p/9800120.html
Abstract:
- 饿汉式:类加载时就创建实例。线程安全。最常用。缺点是可能造成资源浪费。
- 懒汉式:需要实例化时才实例化。实现了延迟加载。缺点是线程不安全。
- 两种方式都是使用private私有化构造方法,使外部不能直接new出这个实例;用一个静态变量存放实例,饿汉是直接把对象赋给变量,懒汉则是在getInstance中实例化变量。
- 因为两种方法各有缺陷,Spring IoC都没采用,而是用的单例注册表的方式来实现。