Servlet 总结

什么是 Servlet?

Servlet 是 Java EE 规范中的核心组件,它是一种运行在服务器端的 Java 程序,主要用于处理客户端请求并作出响应。

Servlet 的执行过程?

  1. 请求发送:客户端发送请求到服务器端。
  2. 请求转发:服务器端根据 web.xml 文件中的 Servlet 相关配置信息,将客户端请求转发到相应的 Servlet。
  3. 业务处理:Servlet 根据 request 对象中封装的用户请求与数据库进行交互;返回数据之后,Servlet 将数据封装到 response 对象中。
  4. 响应返回:控制权从 Servlet 重新回到服务器端,服务器端将响应信息返回给客户端,并跳转到相应的页面。

Servlet 的生命周期?

  1. 加载和实例化:在第一次请求 Servlet 时,Servlet 容器将会创建 Servlet 实例。
  2. 初始化:Servlet 容器加载完成 Servlet 之后,必须进行初始化,此时 init() 方法将被调用。
  3. 服务请求:Servlet 初始化之后,处于响应请求的就绪状态。此时如有客户端请求发送,就会调用 Servlet 实例的 service() 方法,并且根据用户的请求方式,调用 doPost() 或者 doGet() 方法。
  4. 销毁:最后,Servlet 容器负责将 Servlet 实例进行销毁,调用 destroy() 方法实现。

Servlet 与 JSP 的关系及区别?

  • 关系:Servlet 是 JSP 的基础,JSP 是 Servlet 技术的扩展。JSP 运行之前首先会被编译为一个 Servlet。
  • 区别:JSP 侧重于视图(View)展示;Servlet 主要用于控制业务逻辑(Controller)。

Servlet API 中的 forward() 与 redirect() 的区别?

特性请求转发 (forward)重定向 (redirect)
实现方法Request 对象的方法Response 对象的方法
执行位置服务器端执行客户端浏览器执行
请求次数1 次请求2 次请求
地址栏变化不会改变发生改变
数据共享始终在同一个 Request 域中,可共享资源第二次请求将丢失第一次 Request 中的资源
性能优于重定向相对较低

Servlet 如何同时处理多个请求?

Servlet 采用多线程来处理多个请求的同时访问。Servlet 容器通过线程池来管理维护服务请求。所谓线程池,相当于数据库连接池,实际上是等待执行代码的一组线程,叫做工作者线程。Servlet 容器通过一个调度线程来管理工作者线程。

  • 当容器收到一个 Servlet 的访问请求,调度者线程就从线程池中选出一个工作者线程,将用户请求传递给该线程,然后由该线程处理 Servlet 的 service() 方法;
  • 当这个线程在执行的时候,容器收到一个新的请求,调度者线程再次从线程池中选出一个新的工作者线程;
  • 当容器同时收到对同一个 Servlet 的多个请求时,那么 Servlet 的 service() 方法将在多线程中并发执行。

  1. Servlet 容器默认采用单实例多线程的方式来处理请求。这样减少了产生 Servlet 实例的开销,提升了对请求的响应时间。
  2. 对于 Tomcat 容器来讲,可以在其 server.xml 中通过 <Connector> 配置设置线程池中的线程数目。

问题:Servlet 可不可以采用多实例多线程?或者多实例单线程?

如何开发线程安全的 Servlet?

Servlet 容器采用多线程来处理请求,提高性能的同时也造成了线程安全问题。要开发线程安全的 Servlet 应该从以下几个方面进行:

  1. 变量的线程安全:多线程并不共享局部变量,所以我们要尽可能的在 Servlet 中使用局部变量。
  2. 代码块的线程安全:使用同步块 synchronized,防止可能调用的代码块冲突。但是要注意的是,要尽可能缩小同步代码的范围,不要在 service() 方法和响应方法上直接使用同步,这会严重影响性能。
  3. 属性的线程安全:注意 ServletContextHttpSessionServletRequest 对象中属性的安全性。
  4. 使用同步集合:使用 Vector 代替 ArrayList,使用 Hashtable 代替 HashMap
  5. 避免自定义线程:不要在 Servlet 中创建自己的线程来完成某个功能。Servlet 本身就是多线程的,如果再创建新的线程,将会导致线程执行复杂化,出现线程安全问题。
  6. 外部对象互斥:在多个 Servlet 中,对外部对象(比如文件)进行修改操作一定要加锁,做到互斥访问。

SingleThreadModel 接口?

javax.servlet.SingleThreadModel 接口是一个标识接口。如果一个 Servlet 实现了这个接口,那 Servlet 容器将保证在一个时刻仅有一个线程可以在给定的 Servlet 实例的 service() 方法中执行,将其他所有请求进行排队。

如何实现 Servlet 的单线程模式?

实现方法(针对 JSP):

<%@ page isThreadSafe="false" %>

说明

  1. 本文内容基于传统 Servlet 规范整理。
  2. SingleThreadModel 接口已在 Servlet 2.4 版本中被标记为 deprecated(废弃),并在 Servlet 3.0 版本中被移除。现代开发中不建议使用该模式,应通过优化代码逻辑(如避免使用实例变量)来保证线程安全。
  3. 关于同步集合的建议(如 Vector/Hashtable)属于早期 Java 并发编程实践,现代开发推荐使用 java.util.concurrent 包下的并发容器。