单例模式下全局变量出现的问题
springmvc+hibernate+jdbctemplate+mysql
先看下⾯⼩段代码,⼀个controller,⼀个service。
controller.代码:
……..
@Autowired
private XXXService xxxService;
……..
@RequestMapping(“/doXXX.do”)
public void doXXX(){
…..
xxxService.saveXXX(String content,….);
…..
}
XXXService.java代码:
private String content;
……
private void init(){//清空请求参数
content = null;
mvc的controller……
}
public boolean saveXXX(String content, ……){
this.init(content, …);
//业务逻辑处理
}
以上这段代码在访问量不构成并发时不会出现什么问题。 但当⼀个请求还未完成,另⼀个请求已经开始执⾏的情况下就会出现问题(并发): 第⼆个请求执⾏执⾏init()⽅法会将第⼀个请求的content变量设置为null或它本⾝的值,这样数据就被篡改了。
编码者这样写的⽬的是因为content等变量需要在多个⽅法中使⽤,⽽且变量很多,但⼜不想通过⽅法参数的⽅式来传递,故使⽤成员变量。
先看看为什么会出现这种情况。 由于系统采⽤springmvc框架,springmvc核⼼控制器DispatcherServlet 默认为每个controller⽣成单⼀实例来处理所有⽤户请求,所以在这个单⼀实例的controller中,它的XXXService也是⼀个实例处理所有请求, 这样XXXService的成员变量就被所有请求共享。这样就会出现并发请求时变量内容被篡改的问题。
那么出现这种问题如何解决呢?
第⼀种⽅式: 既然是全局变量惹的祸,那就将全局变量都编程局部变量,通过⽅法参数来传递。
第⼆种⽅式: jdk提供了java.lang.ThreadLocal,它为多线程并发提供了新思路。 (当使⽤ThreadLocal维护变量时,ThreadLocal为每个使⽤该变量的线程提供独⽴的变量副本,所以每⼀个线程都可以独⽴地改变⾃⼰的副本,⽽不会影响其它线程所对应的副本)
那么在什么地⽅使⽤ThreadLocal呢? 什么变量是请求公⽤的就将该变量托付给ThreadLocal来管理其线程副本, 所以我们在xxxService中使⽤它。
XXXService.java代码:
private ThreadLocal<String> contentTL = new ThreadLocal<String>();
//private String content;使⽤contentTL代替content;
……
public boolean saveXXX(String content, ……){
/
/业务逻辑处理
//在各⽅法中使⽤content时候⽤()代替
}
此类并发篡改数据的问题,可以在开发⼯具中设置断点调试的⽅式来模拟并发。即第⼀次请求运⾏到断点时,查看content内容,并且不让程序继续往下运⾏,同时再发起⼀个请求,查看content内容。 如内容是第⼀次请求的内容,并且让第⼀个请求跑完后,第⼆个请求到断线处的content正确时,可以确定不会出现并发问题。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论