Spring 中的 Controller 和 Service 的线程安全问题研究
Spring 中的 Controller 和 Service 等其他 Bean 都是默认单例模式的,那是如何确保线程安全的呢?
答案就是,这些 Bean 都是线程不安全的,需要我们自己注意用锁。
- spring单例模式指的是在内存中只实例化一个类的对象。确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
- 当多个用户同时请求一个接口服务的时候,容器会给每一个请求分配一个线程,这时候多个线程会并发执行该请求所对应的业务逻辑(也就是 Controller 或 Service 的方法)。如果该逻辑涉及到对该单例状态(成员变量)的改变,就会有线程安全的问题。
- Spring 可以使用 ThreadLocal 来解决线程安全问题,每个线程去执行业务代码的时候,都会去内存申请临时变量,这样就不会涉及变量并发访问冲突的问题。
- 一般的 Web 应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程。
当然大多数情况下,我们根本不需要考虑线程安全的问题,比如dao,service等,除非在bean中声明了实例变量。因此,我们在使用spring mvc 的contrller时,应避免在controller中定义实例变量。
解决方法:
1、在Controller中使用ThreadLocal变量(推荐)
2、在spring配置文件Controller中声明 scope=“prototype”,每次都创建新的 controller (不推荐)
默认Controller、Dao、Service都是单例的,所以更不推荐 Controller 和 Service 在内部有可以访问更改的线程不稳定的成员对象,注入的 Bean 一般都考虑线程安全问题。默认 Controller 和 Service 都必须是无状态的。
如果在 Spring 中执行多线程任务,推荐用 ThreadPoolTaskExecutor 线程池。