servlet单实例多线程模式

Servlet 的生命周期:

1.        Servlet在 web服务器启动时被加载并实例化,容器运行其 init 方法初始化,请求到达时运行其 service 方法;

2.        service 运行请求对应的 doXXX(doGet,doPost) 方法;

3.        服务器销毁实例,运行其 destory 方法;

Servlet 的生命周期由 Servlet 容器管理;

(三个概念的理解:

Servlet 容器 < Web 容器 < 应用服务器?

Servlet 容器的主要任务就是管理 Servlet 的生命周期;

Web 容器也称之为 web 服务器,主要任务就是管理和部署 web 应用的;

应用服务器的功能非常强大,不仅可以管理和部署 web 应用,也可以部署 EJB 应用,实现容器管理的事务等等。。。

Web 服务器就是跟基于 HTTP 的请求打交道,而 EJB 容器更多是跟数据库,事务管理等服务接口交互,所以应用服务器的功能是很多的。

常见的 web 服务器就是 Tomcat, Tomcat 同样也是 Servlet 服务器;

常见的应用服务器有 WebLogic WebSphere ,但都是收费的;

没有 Servlet 容器,可以用 Web 容器直接访问静态 Html 页面,比如安装了 apache 等;如果需要显示 Jsp/Servlet ,就需要安装一个 Servlet 容器;但是光有 servlet 容器也是不够的,它需要被解析为 html 显示,所以仍需要一个 web 容器;所以,我们常把 web 容器和 Servlet 容器视为一体,因为他们两个容器都有对方的功能实现了,都没有独立的存在了,比如 tomcat

Servlet 是如何处理多个请求同时访问呢?

Servlet 容器默认是采用单实例多线程的方式处理多个请求的:

1.        web 服务器启动的时候(或客户端发送请求到服务器时), Servlet 就被加载并实例化(只存在一个 Servlet 实例);

2.        容器初始化 Servlet 。主要就是读取配置文件(例如 tomcat ,可以通过 servlet.xml <Connector> 设置线程池中线程数目,初始化线程池;通过 web.xml ,初始化每个参数值等等);

3.        当请求到达时, Servlet 容器通过调度线程( Dispatchaer Thread )调度它管理下的线程池中等待执行的线程( Worker Thread )给请求者;

4.        线程执行 Servlet service 方法;

5.        请求结束,放回线程池,等到被调用;

从上面可以看出:

第一: Servlet 单实例,减少了产生 servlet 的开销;

第二:通过线程池来响应多个请求,提高了请求的响应时间;

第三: Servlet 容器并不关心到达的 Servlet 请求访问的是否是同一个 Servlet 还是另一个 Servlet ,直接分配给它一个新的线程;如果是同一个 Servlet 的多个请求,那么 Servlet service 方法将在多线程中并发的执行;

第四:每一个请求由 ServletRequest 对象来接受请求,由 ServletResponse 对象来响应该请求;

问题出现了:

同一个 Servlet 的的多个请求到来时,如果该Servlet中存在成员变量,可能发生多线程同时访问该资源时,都来操作它,造成数据的不一致,因此产生线程安全问题。

解决:

1.        实现 SingleThreadModel 接口

如果一个 Servlet 被这个接口指定 , 那么在这个 Servlet 中的 service 方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题;

2.        同步对共享数据的操作

使用 synchronized 关键字能保证一次只有一个线程可以访问被保护的区段, Servlet 可以通过同步块操作来保证线程的安全。

ServletRequest 对象是线程安全的,但是 ServletContext HttpSession 不是线程安全的;

要使用同步的集合类: Vector 代替 ArrayList HsahTable 代替 HashMap

3.        避免使用实例变量(成员变量)

线程安全问题是由实例变量造成的,只要在 Servlet 里面的任何方法里面都不使用实例变量,那么该 Servlet 就是线程安全的。(所有建议不要在servlet中定义成员变量,尽量用局部变量代替

对上面的三种方法进行测试,可以表明用它们都能设计出线程安全的 Servlet 程序。但是,如果一个 Servlet 实现了 SingleThreadModel 接口, Servlet 引擎将为每个新的请求创建一个单独的 Servlet 实例,这将引起大量的系统开销。 SingleThreadModel Servlet2.4 中 已不再提倡使用;同样如果在程序中使用同步来保护要使用的共享的数据,也会使系统的性能大大下降。这是因为被同步的代码块在同一时刻只能有一个线程执行 它,使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态。另外为保证主存内容和线程的工作内存中的数据的一致性,要频繁地刷新缓存 , 这也会大大地影响系统的性能。所以在实际的开发中也应避免或最小化 Servlet 中的同步代码;在 Serlet 中避免使用实例变量是保证 Servlet 线程安全的最佳选择。从 Java 内存模型也可以知道,方法中的临时变量是在栈上分配空间,而且每个线程都有自己私有的栈空间,所以它们不会影响线程的安全。

  Servlet 的线程安全问题只有在大量的并发访问时才会显现出来,并且很难发现,因此在编写 Servlet 程序时要特别注意。线程安全问题主要是由实例变量造成的 , 因此在 Servlet 中应避免使用实例变量。如果应用程序设计无法避免使用实例变量,那么使用同步来保护要使用的实例变量,但为保证系统的最佳性能,应该同步可用性最小的代码路径。

你可能感兴趣的:(servlet)