目录
前言
Servlet是在服务器端运行的Java程序,可以接收客户端请求并做出响应,是基于 Java 技术的 web 组件,该组件由容器托管,用于生成动态内容。他是用Java编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。
一、servlet生命周期
1、加载并实例化
Servlet容器负责加载和实例化Servelt。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。当Servlet容器启动后,Servlet通过类加载器来加载Servlet类,加载完成后再new一个Servlet对象来完成实例化。
2、初始化
在Servlet实例化之后,容器将调用init()
方法,并传递实现ServletConfig接口的对象。在init()
方法中,Servlet可以部署描述符中读取配置参数,或者执行任何其他一次性活动。在Servlet的整个生命周期类,init()
方法只被调用一次。
3、服务
当Servlet初始化后,容器就可以准备处理客户机请求了。当容器收到对这一Servlet的请求,就调用Servlet的service()
方法,并把请求和响应对象作为参数传递。当并行的请求到来时,多个service()方法能够同时运行在独立的线程中。servlet会在适当的时候调用 doGet()
、doPost()
、doPut(),
doDelete()
等方法。
4、销毁
当Tomcat容器关闭时或由于其他原因导致Servlet需要关闭或卸载时,容器会调用该对象的destroy()方法,以便让Servlet对象可以释放它所使用的资源,该方法同样只会执行一次。在容器调用destroy()方法前,如果还有其他的线程正在service()方法中执行,容器会等待这些线程执行完毕或者等待服务器设定的超时值到达。一旦Servlet对象的destroy()方法被调用,容器会释放这个Servlet对象,在随后的时间内,该对象会被java的垃圾收集器所回收。这四个阶段共同组成了Servlet的生命周期。
5、总结
代码实现:
public class DemoServlet extends HttpServlet{
//初始化servlet,调用init方法
@Override
public void init() throws ServletException {
System.out.println("初始化时调用");
}
//开启服务
@Override
protected void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
System.out.println("开启服务时调用");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
}
//销毁时调用destory
@Override
public void destroy() {
System.out.println("销毁时调用");
}
}
生命周期过程:在一个servlet生命周期中,我们创建servlet实例、调用init()方法、调用destroy()方法,都只会执行一次,而调用service()方法和做出响应会反复执行。
二、线程安全问题
1、servlet是线程不安全的
通过上面的Servlet生命周期可以看出,在Tomcat容器加载并实例化Servlet之后,会创建一个实例,并且这个实例是唯一的,无论多少用户访问Servlet,都共用这一个实例。而每次用户访问Servlet时,服务器都会为每个用户创建一个独立的线程,每个线程都有它自己的堆栈空间。所以说是单实例多线程,这种默认以多线程方式执行的设计可大大降低对系统的资源需求,提高系统的并发量及响应时间,但也同时引发了Servlet的线程安全问题。
对于Servlet中的局部变量,多线程下每个线程对局部变量都会有自己的一份copy,存在自己的堆栈空间中,这样对局部变量的修改只会影响到自己的copy而不会对别的线程产生影响,所以这是线程安全的;对于Servlet中的实例(全局)变量,多线程下所有线程共享实例变量,这一共享就可能导致多个线程之间互相影响,从而引发线程的不安全。
2、解决线程安全问题
(1)不使用实例变量
既然实例变量能引发线程安全问题,那么只要在Servlet类的任何方法里面都不使用实例变量,该Servlet就是线程安全的。事实上,线程安全问题大部分是由实例变量造成的,在Servlet中避免使用实例变量是保证Servlet线程安全的最佳选择。
(2) 使用synchronized
synchronized关键字能保证一次只有一个线程可以访问被保护的区段,所以理论上可以通过同步块操作来保证Servlet的线程安全。但因为其“一次只有一个线程可以访问”的特性,导致当大量用户访问同一资源时,只能排队访问,大量用户处于阻塞状态,这就大大降低了其用户的吞吐量,从而使系统的效率和性能大大降低,不推荐使用此方法
(3) 其他方式
Java的有些集合类也会引发线程安全问题,应避免使用。比如用Vector代替ArrayList,用Hashtable代替HashMap等。另外,不要在Servlet中创建其他线程来完成某个功能,因为Servlet本身就是多线程的,再在Servlet中创建线程,更容易引发线程安全问题。
总结
总的来说Servlet的生命周期主要是四个阶段,加载并实例化,然后调用init()方法进行初始化,初始化完成之后就可以调用service()方法,服务进行完之后,调动destroy()方法销毁,根据生命周期可以看出来,我们的servlet是线程不安全的,希望大家能够通过文章了解到servlet,谢谢。