SpringMVC的线程安全问题
SpringMVC,这个大家每天都在使用的框架,不知大家在使用的时候,是否有考虑过它线程安全的问题呢?我们都知道,SpringMVC通过前端控制器DispatcherServlet来分发处理请求,通过对请求URL和@RequestMapping的映射关系,来调用Controller中对应的方法。Spring的IOC容器中,默认都是单例的,Controller也不例外。服务器肯定是需要接收大量...
SpringMVC,这个大家每天都在使用的框架,不知大家在使用的时候,是否有考虑过它线程安全的问题呢?
我们都知道,SpringMVC通过前端控制器DispatcherServlet来分发处理请求,通过对请求URL和@RequestMapping的映射关系,来调用Controller中对应的方法。
Spring的IOC容器中,默认都是单例的,Controller也不例外。
服务器肯定是需要接收大量请求的,对于多个请求肯定是开启多个线程去并发执行的,否则单线程排队吞吐量就太低了。
多线程并发执行,Controller又是单例,那么必定会存在多线程安全问题。
前言
要讨论接下来的问题,首先需要了解一下JVM的内存模型,知道哪些内存是线程共享的、哪些是线程私有的。
例子
多线程安全无非就是数据共享一致性的问题,对于一个简单的i++操作,下面给出两个例子。
不安全的
public class Demo {
private int i = 0;
void add(){
i++;
System.out.println(i);
}
}
首先i是一个全局变量,和Demo实例一起保存在堆内存中,是线程共享的。
而且i++本身不是一个原子操作,线程在执行每一条JVM指令前都有可能失去CPU的执行权,多个线程去对i进行计算和赋值,最终输出结果将不可预测,出现重复值。
Tips:关于“i++不是原子操作”可以查看笔者的博客:《为什么说i++不是原子操作》。
安全的
public class Demo {
void add(){
int i = 0;
i++;
System.out.println(i);
}
}
现在i是一个局部变量了,保存在线程私有的栈内存中。
每开启一个线程,JVM就会分配一个线程私有的栈空间,方法的执行就是一个个栈帧入栈和出栈的过程。
每个线程的add()入栈时,都会在局部变量表中保存了一个i变量,每个线程操作的都是内部i,也就不存在数据安全问题了。
SpringMVC
在使用SpringMVC时,程序员多少还是需要考虑一下多线程下的数据安全问题。
一般来说,避免去修改Service和Dao,不去对全局变量进行修改,就不会出现数据不一致问题。
局部变量使用起来简单,但是会使得对象被频繁的创建和回收,影响性能消耗内存。
全局变量虽然不会被频繁创建和回收,但是要考虑多线程下数据一致性问题,代码编写复杂,要根据应用实际情况作出取舍。
不安全的
@RestController
public class Demo {
@Autowired
PersonDao personDao;
@Autowired
Person person;
//线程不安全
@RequestMapping("/save")
public void save(String name){
person.setName(name);
personDao.insert(person);
}
}
安全的
@RestController
public class Demo {
@Autowired
PersonDao personDao;
//线程安全
@RequestMapping("/save")
public void save(String name){
Person person = new Person();
person.setName(name);
personDao.insert(person);
}
}
可以使用synchronized关键字,或者利用java.util.concurrent包下的并发工具类来保证数据同步。
测试
@RequestMapping("/test")
public void login_on() {
System.err.println(Thread.currentThread().getName());
}
开启10个线程去并发请求,输出如下:
http-nio-9001-exec-4
http-nio-9001-exec-1
http-nio-9001-exec-2
http-nio-9001-exec-8
http-nio-9001-exec-7
http-nio-9001-exec-6
http-nio-9001-exec-10
http-nio-9001-exec-3
http-nio-9001-exec-9
http-nio-9001-exec-5
频繁创建线程开销是很大的,SpringMVC肯定用到了线程池,使用池中的10个线程来处理请求,而且是基于非阻塞的NIO。
更多推荐
所有评论(0)