1.单例模式的概念
一个对象多次使用,
并且只有方法没有属性,或者有属性,属性不变的,
就可以通过单例模式,私有化对象构造方法,然后通过get对象的方法获取对象。
这样就节省了创建删除对象的性能问题
单例模式要求对象的属性不改变。一般都没有成员属性,就算有也是一个不变的量,比如这个成员变量也是单例模式获得的对象。(SqlSession)
2.默认情况下是单例模式,
3.为什么设计成单例设计模式?
1.性能(不用每次请求都创建对象)
2.不需要多例(不要在控制器中定义成员变量)
4.线程安全问题
在多线程进行访问的时候,有线程安全问题.
但是不建议使用同步,因为会影响性能.
@Controller
@RequestMapping("/demo")
public class MultViewController {
private static int st = 0; //静态的
private int index = 0; //非静态
@RequestMapping("/test")
public void test() {
System.out.println(st++ + " | " + index++);
}
}
默认单例的,随着请求次数的增加:
0 | 0
1 | 1
2 | 2
3 | 3
4 | 4
controller增加注解:
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
改为多例模式此时,无论多少次请求,结果为:
0 | 0
1 | 0
2 | 0
3 | 0
4 | 0
...
从以上很容易看出,单例是线程不安全的,会导致属性的重复性利用。
解读:为什么递增就不安全,为什么static也递增就可以,因为静态成员变量单例和多例的数据变化形式是一样的
而非静态成员变量每次调用本来应该是不会递增的,结果递增了。
5.解决方案
1、不要在controller中定义成员变量。
2、万一必须要定义一个非静态成员变量时候,则通过注解@Scope(“prototype”),将其设置为多例模式
尽量不要在controller里面去定义属性,
如果在特殊情况需要定义属性的时候,
那么就在类上面加上注解@Scope("prototype")改为多例的模式,
以前struts是基于类的属性进行发的,定义属性可以整个类通用,所以默认是多例,
不然多线程访问肯定是共用类里面的属性值的,肯定是不安全的,
但是springmvc是基于方法的开发,都是用形参接收值,一个方法结束参数就销毁了,
多线程访问都会有一块内存空间产生,里面的参数也是不会共用的,
所有springmvc默认使用了单例,所以controller里面不适合在类里面定义属性,
只要controller中不定义属性,那么单例完全是安全的。
springmvc这样设计主要的原因也是为了提高程序的性能和以后程序的维护只针对业务的维护就行,
要是struts的属性定义多了,都不知道哪个方法用了这个属性,对以后程序的维护还是很麻烦的。
5.成员方法也是共享的,为什么就不会出现问题呢?
==================================================
先回答你这个问题:
java 里,每个线程都有自己独享的空间,也就是栈内存。线程在调用方法的时候,会创建一个栈帧。
也就是说调用一个方法的时候,也就是一个栈帧的入栈过程,该方法执行完毕,栈帧也就出栈了。
换句话讲,成员方法对于每个线程事实上是私有的,而不是你表面看上去的那样是 "共享" 的。
那么为什么成员变量会出问题呢?
==================================================
如你所知道的,每个新建对象都存放在堆中,每个持有该对象引用的线程,都可以访问到它(只要你有那个权限)。
这也就是说,成员变量对于每个线程,事实上是共享的。
看不懂就记住结论,在单例模式里,方法是私有的,成员变量是共享的,所以方法是安全的,变量是共享的
所以我们注入Service不会存在线程安全问题,因为你只是调用Service里的方法,方法都是线程安全的,多线程调用一个实例的方法,会在内存中复制变量,所以只要你不在Constroller里修改Service这个实例就没问题。
相关问题
1.详细解析:
https://blog.csdn.net/qq_27026603/article/details/67953879
2.springmvc-controller成员变量注入HttpServletRequest线程安全吗?
1.在controller中注入的request是jdk动态代理对象,
ObjectFactoryDelegatingInvocationHandler的实例.
当我们调用成员域request的方法的时候其实是调用了objectFactory的getObject()对象的相关方法.
这里的objectFactory是RequestObjectFactory.
2.RequestObjectFactory的getObject其实是从RequestContextHolder的threadlocal中去取值的.
3.请求刚进入springmvc的dispatcherServlet的时候会把request相关对象
设置到RequestContextHolder的threadlocal中去.
https://blog.csdn.net/wrongyao/article/details/85088591