概要
Servlet 默认是单例模式,在web 容器中只创建一个实例,所以多个线程同时访问servlet的时候,Servlet是线程不安全的。
那么 web 容器能为每个请求创建一个Servlet的实例吗?当然是可以的,只要Servlet实现SingleThreadModel接口,就可以了。
SingleThreadModel
该接口为每次请求创建一个servlet实例。此接口没有方法,跟Serializable接口一样只是一个标识接口。
注意,singlethreadmodel并不能解决所有的线程安全问题。例如,会话属性和静态变量仍然可以同时通过多线程的多个请求访问,即使用实现SingleThreadModel 接口的 servlet。建议开发人员采取其他方法来解决这些问题,而不是实现此接口,
例如避免使用实例变量或同步访问这些资源的代码块。
SingleThreadModel这个接口 Servlet API 2.4版本过时,不推荐大家使用了因为它存在性能问题,下面会介绍。
SingleThreadModel 使用示例
import javax.servlet.SingleThreadModel
public class MyServlet extends HttpServlet implements SingleThreadModel {
只要Servlet实现 SingleThreadModel 接口就可以了。
Servlet 对象创建 源码分析
org.apache.catalina.core.StandardWrapper类是对应一个Servlet的容器,下面我们分析StandardWrapper是怎么创建Servlet实例的。
allocate() 创建Servlet 实例
首先判断当前servlet是不是 SingleThreadModel,如果不是,则使用双重检查的方式创建 instance 单例实例。 通过调用loadServlet方法进行创建 instance。
- 只要Servlet不是SingleThreadModel,则创建Servlet的单例实例
如果第一次访问Servlet,则singleThreadModel属性默认是false,需要调用loadServlet方法加载Servlet后才能判断该Servlet是不是SingleThreadModel模式。
- 如果是SingleThreadModel模式并且是新创建的实例,则把当前instance添加到instancePool中,并nInstances++。
- 如果不是SingleThreadModel模式,则更新countAllocated+1,并且返回Servlet的instance实例
只有SingleThreadMode才能到synchronized块,因为,不是SingleThreadMode的已经执行return方法了。
判断当前Servlet创建的实例数量是否超过了maxInstances数量,默认maxInstances=20
如果超过当前则wait等待,否则,调用loadServlet()创建Servlet实例并添加到instancePool中。并更新nInstances数量。
- 从这里可以看出每次都会调用loadServlet()方法来创建Servlet实例对象的。
SingleThreadMode 性能问题
从这里看出如果使用SingleThreadMode 模式,有两处性能问题
- 每个Servlet 创建多个对象实例
- 如果并发高,每个servlet同时只能支持20线程的并发访问。挂起超过20个的线程。
loadServlet() 方法
- 创建Servlet实例对象
- 判断该Servlet 是不是 SingleThreadMode
- 初始化Servlet
想了解更多精彩内容请关注我的公众号