关于SprimgMVC中request的线程安全
来源:互联网 发布:安卓编程教程 编辑:程序博客网 时间:2024/06/05 01:16
我们知道springmvc中request是方法级别的,一个方法对应一个request。那么如果我们把request设置为类级别的变量呢?就像这样:
@Controller@RequestMapping("/admin")public class AdminController { private HttpServletRequest tempRequest; @ModelAttribute public void getRequest(HttpServletRequest request){ // 每个方法执行前都将当前的request赋给tempRequest tempRequest = request; } ......}
根据Sprimg的默认规则,controller对于factorybean默认是单例,controller只有一个。我们知道tomcat对于每一个请求都会生成一个处理线程去处理当前请求,那么在并发量高的情况下,我们都去请求例如:public ModelAndView checkAdmin() 这样一个方法,因为我们设置request为类变量,那么势必会造成后一个request覆盖了前一个request的一些请求参数。如果下面这个例子:
private HttpServletRequest tempRequest; @ModelAttribute public void getRequest(HttpServletRequest request){ // 每个方法执行前都将当前的request赋给tempRequest tempRequest = request; } @RequestMapping("/checkAdmin") public void checkAdmin() { try{ // 这里让请求线程等待10秒,模拟高并发 Thread.currentThread().sleep(10000); }catch(InterruptedException e){} String num = tempRequest.getParameter("num"); System.out.println("请求" + tempRequest.hashCode() + ", num = " + num); }
然后我们在10秒内分别请求:
http://localhost:8081/eHow/admin/checkAdmin?num=1http://localhost:8081/eHow/admin/checkAdmin?num=2http://localhost:8081/eHow/admin/checkAdmin?num=3
先看结果:
请求217031088, num = 3请求217031088, num = 3请求217031088, num = 3
照道理,应该request 的hashcode(内存地址)不一样,num也不一样。但是通过结果可以发现,虽然说每次请求num都不一样,但是打印结果确是request hashcode相同,num也一样。这是因为我们让请求线程睡眠了10s,10s后再去从tempRequest取值,那么其实tempRequest已经被最后一个覆盖,三次请求其实最后取的都是同一个tempRequest。也就是说,这样设计的request是线程不安全的,并不是将当前request放在当前线程threadlocal中。(threadlocal简单理解就是将一些值与当前线程关联起来,放的时候放在当前线程中,取得时候就从当前线程中去取。)
ok,那么我们试一下,将每次请求的request放在threadlocal中。
// private HttpServletRequest tempRequest; // 这个threadlocal就是放当前线程的HttpServletRequest变量 private static ThreadLocal<HttpServletRequest> local = new ThreadLocal<HttpServletRequest>(); @ModelAttribute public void getRequest(HttpServletRequest request){ // 将当前request放入threadlocal中 local.set(request); } @RequestMapping("/checkAdmin") public void checkAdmin() { try{ Thread.currentThread().sleep(10000); }catch(InterruptedException e){} // 取的时候从当前的线程中去取request HttpServletRequest request = local.get(); String num = request.getParameter("num"); System.out.println("请求" + request.hashCode() + ", num = " + num); }
请求地址还是和上面一样,看一下结果:
请求2114778512, num = 1请求693198960, num = 2请求973234962, num = 3
可以看到,hashcode不一样,num也不同,也就是说确实将每次请求的request放在了当前的线程中,取得时候也可以从当前线程中取得,这样就不存在覆盖的问题,所以说这种方式是线程安全的。
spring应该是考虑到了这一点,所以给我们提供了一个HttpServletRequest的代理bean,通过@AutoWired注入。具体的实现方式其实和我们上面threadlocal差不多。因为controller是单例,所以我们注入一个httpservletrequest的代理bean,当有一个新的请求进来时,就判断当前线程中有没有httpservletrequest对象,没有就在threadlocal中放一个httpservletrequest对象,当我们去取request的一些参数时,代理bean就从当前线程中的httpservletrequest去取。这样就保证了线程安全。
我们试一下:
// 注入代理类 @Autowired private HttpServletRequest tempRequest; // 这个threadlocal就是放当前线程的HttpServletRequest变量 // private static ThreadLocal<HttpServletRequest> local = new ThreadLocal<HttpServletRequest>(); // @ModelAttribute // public void getRequest(HttpServletRequest request){ // // 将当前request放入threadlocal中 // local.set(request); // } @RequestMapping("/checkAdmin") public void checkAdmin() { try{ Thread.currentThread().sleep(10000); }catch(InterruptedException e){} String num = tempRequest.getParameter("num"); System.out.println("请求" + tempRequest.hashCode() + ", num = " + num); }
请求地址还是和上面一样,看一下结果:
请求368958259, num = 1请求368958259, num = 2请求368958259, num = 3
咦,为什么hashcode一样,num不一样,跟上面结果不同?我们刚才说到request在去取参数也就是tempRequest.getParameter(“num”) 的时候代理bean会从当前threadlocal去取request。
我们贴一部分这个代理bean相关的源码,看一下原因:
// 这个就是代理的request bean private final ObjectFactory objectFactory; public ObjectFactoryDelegatingInvocationHandler(ObjectFactory objectFactory) { this.objectFactory = objectFactory; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (methodName.equals("equals")) { return (proxy == args[0]); } else if (methodName.equals("hashCode")) { // 可以看到当方法为hashcode的时候,就会取出代理的hashcode // 所以hashcode都是一样的 return System.identityHashCode(proxy); } else if (methodName.equals("toString")) { return this.objectFactory.toString(); } try { // 如果方法是其他 // ObjectFactory的getObjcect就是从当前threadlocal中取数据 return method.invoke(this.objectFactory.getObject(), args); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } }
private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable { public ServletRequest getObject() { // currentReuqestAttributes负责从Threadlocal中获取对象 return currentRequestAttributes().getRequest(); } @Override public String toString() { return "Current HttpServletRequest"; } }
ok,也就是说除了equals、hashcode以及toString以外的其他方法都是从threadlocal中取。所以才会造成上面hashcode一样,num不一样的情况。所以这样也是线程安全的。
还有一种线程安全,就是方法级别的reqeust。
// 注入代理类 // @Autowired // private HttpServletRequest tempRequest; // 这个threadlocal就是放当前线程的HttpServletRequest变量 // private static ThreadLocal<HttpServletRequest> local = new ThreadLocal<HttpServletRequest>(); // @ModelAttribute // public void getRequest(HttpServletRequest request){ // // 将当前request放入threadlocal中 // local.set(request); // } @RequestMapping("/checkAdmin") public void checkAdmin(HttpServletRequest request){ try{ Thread.currentThread().sleep(10000); }catch(InterruptedException e){} String num = request.getParameter("num"); System.out.println("请求" + request.hashCode() + ", num = " + num); }
看结果:
请求1814088687, num = 1请求2071623777, num = 2请求1324577120, num = 3
可以看到每次输出也是不同的。因为每次请求都是一个不同的request,每个request都是方法级别的,独享的,也就不存在前面覆不覆盖的问题了。
以上就是对springmvc request线程安全的一些测试。
- 关于SprimgMVC中request的线程安全
- ATL中关于线程安全的优化
- JAVA中关于线程安全
- linux中关于时间函数的线程安全
- 关于线程安全的笔记
- 关于PHP线程安全和非线程安全的区别
- 关于PHP线程安全和非线程安全的区别
- SpringMVC中request的线程安全问题
- 关于ConcurrentLinkedQueue是否线程安全的疑问
- 关于线程安全的一些记录
- 关于Servlet的生命周期和线程安全
- 关于stlport的线程安全诠释
- 关于Android的UI非线程安全
- 关于STL容器的线程安全特性
- 关于Servlet线程安全的详解
- 关于php线程安全的一些东西
- 关于线程安全的单例模式
- 关于java 线程安全的总结
- 数字金字塔(动态规划)
- 快速排序
- 用分布式压缩贴图加快 Unity3D 的打包过程
- Android性能优化典范
- Android之热修复
- 关于SprimgMVC中request的线程安全
- 通过Uri获取手机通讯录的某个联系人
- JS正则表达式验证数字
- 01背包问题
- Sensor框架Framework层解读
- centos7 将网卡配置成桥接模式
- Linux上下行网速测试工具_speedtest-cli
- Windows Redis配置不生效解决方案
- Redis工具类对各种数据类型的操作