Servlet 和 Spring MVC 是不是线程安全的

太长不看:默认情况下 servlet 不是线程安全的;Spring bean 是否线程安全取决于这个 bean 的 scope。

Servlet 为什么不是线程安全的

单个 servlet 实例中的方法会被多个线程同时调用很多次,而 servlet 类中的变量是引用传递,多个线程同时存取某个变量时,就会产生线程安全问题。

以下方法可以让 servlet 或其中的一部分代码块变成线程安全的:

  • 使用 syncronized 关键字。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class MyServlet extends HttpServlet {

public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//pre-processing
synchronized (this) {
//code in this block is thread-safe
}
//other processing;
}
}
  • 使用 SingleThreadModel

如果在 Sun Java System Web Server 7.0 中部署单线程 servlet 时,servlet 引擎会创建一个 servlet 实例池,并保持内存中有多个 servlet 实例的副本。要想调整实例池中的实例数量,可以调整 Web Server 中的 singleThreadedServletPoolSize 属性。这种情况下,服务器的性能可能会降低,如果池中的所有实例全部被占用,那么新到来的请求就必须要在队列中等待某个实例被释放。

1
2
3
4
5
6
7
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class myServlet extends HttpServlet implements SingleThreadModel {
//servlet methods...
}

Spring Bean 什么时候是线程安全的,什么时候不是

首先,singleton 的 bean 不是线程安全的。在不使用 @Lazy 注解时,框架会在启动时就初始化好 singleton bean。但是框架不会管理开发人员怎么用这些 singleton bean,换言之,开发人员要自己处理线程安全问题。

而被 @RequestScope 注解标记的 bean 是线程安全的,因为这些 bean 不会在线程之间共享,而是在每次请求到来时都会创建新的实例。

Session scope 的 bean 也不是线程安全的,因为这些 bean 会与用户的会话绑定,每有一个新用户发来请求,就会创建一个新的 bean。但是,用户发来的请求可能是并行的,所以也有可能产生线程安全问题。

Prototype bean 是不是线程安全取决于它与哪种 bean 绑定。如果它是被一个 singleton bean 所依赖,因为 singleton bean 不是线程安全的,这个 prototype bean 也将被多个线程访问,所以此时 prototype bean 不是线程安全的;如果它被一个 request scope 的 bean 所依赖,那么这个 prototype bean 就是线程安全的,因为它会随着 request scoped bean 消亡而消亡,同时不会被多个线程共享。

除此之外,如果一个 bean 是无状态的,那么无论是什么 scope,它都是线程安全的。

参考文档

  • Handling Threading Issues
  • Spring bean thread safety guide