0%

servlet的线程安全问题

讲servlet之前要先讲一下servlet容器,
Servlet容器是web server或application server的一部分,供基于请求/响应发送模型的网络服务,解码基
于 MIME 的请求,并且格式化基于 MIME 的响应。Servlet 容器也包含并管理 Servlet 生命周期。
所以对于servlet的线程模型和它所依赖的容器有关系。
tomcat是最常用的servlet容器。

Tomcat介绍

Tomcat是Apache基金会下的一个开源项目。。。。。。。。。。

实现了对Servlet和JSP的支持,并提供了作为Web服务器的一些特有功能

Tomcat提供了一个Jasper编译器用以将JSP编译成对应的Servlet。

(上面都是废话)

Tomcat的处理过程

Tomcat对servlet的处理的过程是:

  1. 客户端第一次请求Servlet的时候,tomcat会根据web.xml配置文件实例化servlet
  2. 当又有一个客户端访问该servlet的时候,不会再实例化该servlet

tomcat自己维护了一个线程池,在收到请求时,会到池子里找可用的线程。如果找不到可用的线程,并且线程池已经到了最大线程数,那么请求就会放到一个队列中等待获取可用的线程。

举个栗子:
创建一个Servlet,每次去请求都打印出来自己的hashcode

1
2
3
4
5
6
7
public class MyServlet extends HttpServlet{
private Logger logger = Logger.getLogger(this.getClass());
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
logger.debug(this.hashCode());
}
}

结果是这样的:

会发现每次请求的servlet都是同一个,也就是每个servlet只有一个实例。

所以servlet是线程不安全的,如果使用静态或者实例变量中存储请求和响应对象,那么在多线程去情况下一定会出现问题。

设计线程安全的Servlet

实现SingleThreadModel接口

SingleThreadModel接口是一个标识接口,如果实现了这个接口,那么Tomcat就会保证一个时刻只有一个线程对这个servlet进行操作,其他的线程排着队慢慢来。

这种方式虽然安全,但是太低效了。
因此Servlet的规范中已经被废弃了。

使用同步的类

对集合进行操作的时候,选则同步的集合就不会有线程不安全的问题了

Synchronized关键字

多个servlet对外部对象进行操作,一定要上锁,这种锁机制效率也很慢。

使用局部变量

变量都是局部变量,用完就回收了,再用再创建。

属性的线程安全

ServletContext是线程不安全的,多线程下可以同时进行读写,因此我们要对其读写操作进行同步或者深度的clone。

HttpSession是线程不安全的,和ServletContext的操作一样。