1. springMVC-controller 中的并发问题
    在Tomcat容器中,每个servlet是单例的。在SpringMVC中,Controller 默认也是单例。 采用单例模式的最大好处,就是可以在高并发场景下极大地节省内存资源,提高服务抗压能力(不用每次创建Controller,减少了对象创建和垃圾收集的时间。)。
    Spring中的Bean默认都是单例。

当有多个用户同时请求一个服务时,容器会给每一个请求分配一个线程,各个线程是不会相互影响,

单例模式容易出现的问题是:在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容器会生成并装配这些对象;当该对象不再处于这些使用场景的限定时,容器通常会销毁这些对象。

Bean的 Scope 属性设置为 prototype 的话,容器在接受到该类型对象的请求时,每次都会重新生成一个新的对象给请求方。

  • singleton 表示在spring容器中的单例,通过spring容器获得该bean时总是返回唯一的实例
  • prototype 表示每次获得bean都会生成以新的对象
  • request 表示在一次Htttp请求内有效(只适用于web应用)
  • session 表示在一个用户会话内有效(只适用于web应用)
  • globalSession 表示在全局会话内有效(只适用于web应用)

另外可以在Controller 中使用 ThreadLocal 变量。 每一个线程都有一个变量的副本。更严格的做法是用AtomicInteger类型定义成员变量,对于成员变量的操作使用AtomicInteger的自增方法完成。

单例的状态改变:体现为该单例的成员属性值的修改
有状态对象(Stateful Bean):就是有成员变量的对象,可以保存数据,是非线程安全的
无状态对象(Stateless Bean):就是没有实例变量的对象,不能保存数据,是不变类,是线程安全的
Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态。
浏览器访问服务器时将会产生一个session,同时会产生多个线程来处理请求。