Spring MVC 线程安全问题的思考
在读一些博文的时候发现有些文章对SpringMVC的Controller线程安全的验证并不正确,比如没有探究controller线程不安全的具体原因,比如将请求线程当做controller多例的证明,以下将验证。 http://bbs.csdn.net/topics/390894585 http://bbs.csdn.net/topics/390891861#post-398241838
1.request请求线程与controllte的关系是什么?
简易的验证方法代码
在网上随便下载了一个压力测试工具
测试结果如下
从打印的日志可以看出,请求时多线程请求的,但是每次请求过来调用的Controller对象都是一个,而不是一个请求过来就创建一个controller对象
那为什么说controller是不安全的呢?
原因就在于如果这个controller对象是单例的,那么如果不小心在类中定义了类变量,那么这个类变量是被所有请求共享的,这可能会造成多个请求修改该变量的值,出现与预期结果不符合的异常。
接下来将验证多并发的情况下controller的线程不安全的具体表现、通过配置实现controller多例
在单例的情况下 相当于所有类变量对于每次请求都是共享的,每一次请求对类变量的修改都是有效的
- private static int st=0;
- private int index=0;
- @RequestMapping(value="/testcontrollersinglon",method=RequestMethod.GET)
- @ResponseBody
- public String testControllerSinglon(HttpServletRequest request){
- try {
- System.out.println(st++ + " | " + index++);
- return "yes";
- } catch (Exception e) {
- e.printStackTrace();
- return "error";
- }
- }
看一下打印的日志
通过日志可以看出 变量index为所有请求共享
那有没有办法让controller不以单例而以每次请求都重新创建的形式存在呢?
答案是当然可以,只需要在类上添加注解@Scope("prototype")即可,这样每次请求调用的类都是重新生成的(每次生成会影响效率)
添加@Scope("prototype")后我们再看看日志就会看出区别了
虽然这样可以解决问题,但增加了时间成本,总让人不爽,还有其他方法么?答案是肯定的!使用ThreadLocal 来保存类变量,将类变量保存在线程的变量域中,让不同的请求隔离开来