作者 | 微说互联网

来源 | https://www.toutiao.com/i6927297421139706376/

单例模式(Singleton)是程序设计中一种非常重要的设计模式,设计模式也是Java面试重点考察的一个方面。面试经常会问到的一个问题是:SpringMVC中的Controller是单例还是多例,很多同学可能会想当然认为Controller是多例,其实不然。

官网截图

根据Tomcat官网中的介绍,对于一个浏览器请求,tomcat会指定一个处理线程,或是在线程池中选取空闲的,或者新建一个线程。

Each incoming request requires a thread for the duration of that request. If more simultaneous requests are received than can be handled by the currently available request processing threads, additional threads will be created up to the configured maximum (the value of the maxThreads attribute). If still more simultaneous requests are received, they are stacked up inside the server socket created by the Connector, up to the configured maximum (the value of the acceptCountattribute). Any further simultaneous requests will receive "connection refused" errors, until resources are available to process them.

—— https://tomcat.apache.org/tomcat-7.0-doc/config/http.html

在Tomcat容器中,每个servlet是单例的。在SpringMVC中,Controller 默认也是单例。 采用单例模式的最大好处,就是可以在高并发场景下极大地节省内存资源,提高服务抗压能力。

单例模式容易出现的问题是:在Controller中定义的实例变量,在多个请求并发时会出现竞争访问,Controller中的实例变量不是线程安全的。

Controller不是线程安全的

正因为Controller默认是单例,所以不是线程安全的。如果用SpringMVC 的 Controller时,尽量不在 Controller中使用实例变量,否则会出现线程不安全性的情况,导致数据逻辑混乱。

举一个简单的例子,在一个Controller中定义一个非静态成员变量 num 。通过Controller成员方法来对 num 增加。

@Controller
public class TestController {private int num = 0;@RequestMapping("/addNum")public void addNum() {System.out.println(++num);}
}

在本地运行后:

  • 首先访问 http:// localhost:8080 / addNum,得到的答案是1;

  • 再次访问 http:// localhost:8080 / addNum,得到的答案是 2。

  • 两次访问得到的结果不同,num已经被修改,并不是我们希望的结果,接口的幂等性被破坏。

从这个例子可以看出,所有的请求访问同一个Controller实例,Controller的私有成员变量就是线程共用的。某个请求对应的线程如果修改了这个变量,那么在别的请求中也可以读到这个变量修改后的的值。

Controller并发安全的解决办法

如果要保证Controller的线程安全,有以下解决办法:

  • 尽量不要在 Controller 中定义成员变量 ;

  • 如果必须要定义一个非静态成员变量,那么可以通过注解 @Scope(“prototype”) ,将Controller设置为多例模式。

@Controller
@Scope(value="prototype")
public class TestController {private int num = 0;@RequestMapping("/addNum")public void addNum() {System.out.println(++num);}
}

Scope属性是用来声明IOC容器中的对象(Bean )允许存在的限定场景,或者说是对象的存活空间。在对象进入相应的使用场景之前,IOC容器会生成并装配这些对象;当该对象不再处于这些使用场景的限定时,容器通常会销毁这些对象。

Controller也是一个Bean,默认的 Scope 属性为Singleton ,也就是单例模式。如果Bean的 Scope 属性设置为 prototype 的话,容器在接受到该类型对象的请求时,每次都会重新生成一个新的对象给请求方。

  • Controller 中使用 ThreadLocal 变量。 每一个线程都有一个变量的副本。

public class TestController {private int num = 0;private final ThreadLocal <Integer> uniqueNum =new ThreadLocal <Integer> () {@Override protected Integer initialValue() {return num;}};@RequestMapping("/addNum")public void addNum() {int unum = uniqueNum.get();uniqueNum.set(++unum);System.out.println(uniqueNum.get());}
}

以上代码运行以后,每次请求 http:// localhost:8080 / addNum , 得到的结果都是1。

更严格的做法是用AtomicInteger类型定义成员变量,对于成员变量的操作使用AtomicInteger的自增方法完成。

总的来说,还是尽量不要在 Controller 中定义成员变量为好。

- END -

往期推荐

求职必备技能:教你如何扒了公司的底裤!

帮你朋友进来看看:色情片伤害人体的全过程

建议被降级降薪员工主动辞职?网友炸了!

如何轻松搞定CRUD的创建人、修改人、时间等字段的赋值

架构师必备技能:Maven Archetype生成项目模板

技术交流群

最近有很多人问,有没有读者交流群,想知道怎么加入。加入方式很简单,有兴趣的同学,只需要点击下方卡片,回复“加群“,即可免费加入我们的高质量技术交流群!

点击阅读原文,直达教程目录

SpringMVC:如何保证Controller的并发安全相关推荐

  1. springMVC 不扫描 controller 中的方法

    最近把之前的一个Maven项目在一个新的电脑环境上导入Eclipse,启动时却发现不扫描 controller 中的方法 下面是正确的 spring-mvc.xml 文件 <?xml versi ...

  2. SpringMVC自定义注入controller变量

    问题描述 在SpringMVC中默认可以注入Model,ModelAndView,@RequestParam,@PathVariable 等,那么这个是怎么实现的,以及怎么注入一个自定义的参数呢 Ha ...

  3. (转)SpringMVC学习(七)——Controller类的方法返回值

    http://blog.csdn.net/yerenyuan_pku/article/details/72511844 本文所有案例代码的编写均建立在前文SpringMVC学习(六)--SpringM ...

  4. SpringMVC中的Controller默认单例

    众所周知,Servlet是单例的. 在struts中,Action是多例的,每一个请求都会new出来一个action来处理. 在Spring中,Controller默认是单例的,多个请求都会访问同一个 ...

  5. springMVC中同一个controller之间方法的跳转以及不同controller之间的跳转

    1.同一个controller里面方法的跳转 @RequestMapping(value="/demo1")public ModelAndView demo1(){System.o ...

  6. Java 17正式发布, Oracle宣布免费提供!“版本任你发,我用Java 8”或成历史?...

    Java 最重要的一次更新:JAVA 17 正式发布,这是一个LTS(长期支持)版本,带来了不少有用的新特性.关于Java 17 的一些新特性,有兴趣的可以移步文章 Java 17 新特性确定 . O ...

  7. 十年开发,连登陆接口都写这么烂...

    点上方蓝色小字,关注,记得星标哦 关注公众号后台回复pay或mall获取实战项目资料+视频 作者:哒哒哒哒打代码 juejin.cn/post/6859214952704999438 前言 大家学写程 ...

  8. Linux 桌面版太“惨”了。。。

    点关注公众号,回复"1024"获取2TB学习资源! 编译 | 苏宓 出品 | CSDN(ID:CSDNnews) Linux 诞生的 31 年间,作为一款开源且免费的操作系统,称霸 ...

  9. Spring Bean的那些事儿

    目录 bean的作用域 beanName.alias beanName的默认生成策略 AnnotationBeanNameGenerator DefaultBeanNameGenerator 创建be ...

最新文章

  1. 用淘宝购买的win7 U盘系统给苹果笔记本Mac OS安装双系统
  2. 消灭Bug!推荐7款优秀的开源Bug跟踪工具
  3. java 通配符 泛型_java中泛型之类型通配符(?)
  4. 开发日记-20190821 关键词 读书笔记《掌控习惯》DAY 1
  5. 百度API地图的标注不居中显示,而显示在左上角
  6. wxWidgets:wxDirPickerCtrl类用法
  7. php 相加 机组数字,PHP-80型等压比例混合器
  8. 10月13日学习内容整理:线程,创建线程(threading模块),守护线程,GIL(全局解释器互斥锁)...
  9. linux vps 命令,CentOS最常用Linux vps操作命令整理大全
  10. 向量二次规划matlab,MATLAB中使用Opti Toolbox的混合整数二次规划
  11. 广播接收者android,电话拦截广播,电话接收者demo
  12. React Native/Android Studio (mac ) 查看 APP界面 对应 代码页 快捷方法
  13. java 汉字按拼音排序_Java当中汉字按照拼音排序
  14. CNN结构:色彩特征提取-从RGB空间到HSV空间(色彩冷暖判断)
  15. oracle cdb能存数据嘛,ORACLE 12C 非CDB迁移CDB之克隆非CDB数据库
  16. Linux课程之linux的发展
  17. [SSM框架]—Spring入门
  18. 黏贴图片到word文档图片显示不全,只显示一行(保姆级图文)
  19. oracle spatial 论文,oraclespatial在实际应用中的六大功能体现
  20. ZEN mining

热门文章

  1. Exchange 2013部署系列之(七)配置SSL多域名证书
  2. 广域网安全建设的思路和部署
  3. 大型局域网二层三层结构比较
  4. golang 切片 slice 去掉重复元素
  5. linux 内核参数somaxconn TCP监听队列长度
  6. linux c 守护进程创建原理及简易方法
  7. GetLocaleInfo和本地化
  8. 100多个Android Demo的整合
  9. Android开发--图形图像与动画(三)--Animation效果的XML实现
  10. AWS之EC2实例搭建LAMP服务器