描述Servlet生命周期,Servlet是线程安全的吗,为什么?

一、Servlet基础

        1.运行在Servlet容器中的Java类,可接受请求并产生响应。

        2.Servlet API

                a.Servlet接口

                b.ServletConfig接口

                c.GenericServlet / HttpServlet抽象类

                d.ServletContext接口

                f.ServletRequest / HttpServletRequest接口

                g.ServletResponse / HttpServletResponse接口

        3.开发Servlet

                a.继承HttpServlet

                b.重写init()方法,doGet()方法,doPost()方法,destroy()方法

        4.Servlet生命周期

                a.加载和实例化

               Servlet容器负责加载和实例化Servlet。当Servlet容器启动时,或者在容器检测到需要这个Servlet来响应第一个请求时,创建Servlet实例。当Servlet容器启动后,它必须要知道所需的Servlet类在什么位置,Servlet容器可以从本地文件系统、远程文件系统或者其他的网络服务中通过类加载器加载Servlet类,成功加载后,容器创建Servlet的实例。因为容器是通过Java的反射API来创建Servlet实例,调用的是Servlet的默认构造方法(即不带参数的构造方法),所以我们在编写Servlet类的时候,不应该提供带参数的构造方法。

                b.初始化

                        (1)init()方法

                        (2)只执行一次

                在Servlet实例化之后,容器将调用Servlet的init()方法初始化这个对象。初始化的目的是为了让Servlet对象在处理客户端请求前完成一些初始化的工作,如建立数据库的连接,获取配置信息等。对于每一个Servlet实例,init()方法只被调用一次。在初始化期间,Servlet实例可以使用容器为它准备的ServletConfig对象从Web应用程序的配置信息(在web.xml中配置)中获取初始化的参数信息。在初始化期间,如果发生错误,Servlet实例可以抛出ServletException异常或者UnavailableException异常来通知容器。ServletException异常用于指明一般的初始化失败,例如没有找到初始化参数;而UnavailableException异常用于通知容器该Servlet实例不可用。例如,数据库服务器没有启动,数据库连接无法建立,Servlet就可以抛出UnavailableException异常向容器指出它暂时或永久不可用。

                c.服务

                        (1)service()方法

                        (2)反复执行

                Servlet容器调用Servlet的service()方法对请求进行处理。要注意的是,在service()方法调用之前,init()方法必须成功执行。在service()方法中,Servlet实例通过ServletRequest对象得到客户端的相关信息和请求信息,在对请求进行处理后,调用ServletResponse对象的方法设置响应信息。在service()方法执行期间,如果发生错误,Servlet实例可以抛出ServletException异常或者UnavailableException异常。如果UnavailableException异常指示了该实例永久不可用,Servlet容器将调用实例的destroy()方法,释放该实例。此后对该实例的任何请求,都将收到容器发送的HTTP 404(请求的资源不可用)响应。如果UnavailableException异常指示了该实例暂时不可用,那么在暂时不可用的时间段内,对该实例的任何请求,都将收到容器发送的HTTP 503(服务器暂时忙,不能处理请求)响应。

                d.销毁

                        (1)destroy()方法

                        (2)只执行一次

                当容器检测到一个Servlet实例应该从服务中被移除的时候,容器就会调用实例的destroy()方法,以便让该实例可以释放它所使用的资源,保存数据到持久存储设备中。当需要释放内存或者容器关闭时,容器就会调用Servlet实例的destroy()方法。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收。如果再次需要这个Servlet处理请求,Servlet容器会创建一个新的Servlet实例。
 

        以上是Servlet生命周期的几个阶段,下面我们来用图示说明Servlet生命周期

        5.jsp本质上是一个Servlet,jsp适合表示层开发,Servlet适合封装控制逻辑

二、Servlet线程

        Servlet线程不安全,Servlet是单实例多线程的,当多个线程同时访问同一个Servlet时,web

服务器会为每一个客户的访问请求创建一个线程,并且在这个线程上调用Servlet的service方法,

因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。

@WebServlet("/lizi")
public class lizi extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
    
    public lizi() {
        super();
        // TODO Auto-generated constructor stub
    }
    
    
	private int count;

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

		        for(int i=0;i<100;i++) {
		        try {
		            Thread.sleep(50);
		            count++;
		        } catch (InterruptedException e) {
		            e.printStackTrace();
		        }
		        System.out.println("This is the No. " + count + " request" + ", Current Thread is :" + Thread.currentThread().getName());
		    }
	}
		

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		this.doGet(request, response);
	}

}

   从运行结果来看,我们能看到有多个14请求运行,所以说明线程不安全。

​​​​​​This is the No. 1 request, Current Thread is :http-nio-8080-exec-1
This is the No. 2 request, Current Thread is :http-nio-8080-exec-2
This is the No. 3 request, Current Thread is :http-nio-8080-exec-3
This is the No. 5 request, Current Thread is :http-nio-8080-exec-4
This is the No. 6 request, Current Thread is :http-nio-8080-exec-5
This is the No. 6 request, Current Thread is :http-nio-8080-exec-6
This is the No. 9 request, Current Thread is :http-nio-8080-exec-7
This is the No. 9 request, Current Thread is :http-nio-8080-exec-8
This is the No. 10 request, Current Thread is :http-nio-8080-exec-9
This is the No. 12 request, Current Thread is :http-nio-8080-exec-10
This is the No. 14 request, Current Thread is :http-nio-8080-exec-1
This is the No. 14 request, Current Thread is :http-nio-8080-exec-2

 三、那么如何解决线程安全问题呢?有以下几个方法:

        1.实现SingleThreadModel接口

         该接口指定了系统如何处理对同一个Servlet的调用。如果一个Servlet被这个接口指定,那么在

这个Servlet中的service方法将不会有两个线程被同时执行,当然也就不存在线程安全的问题。

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

        使用synchronized关键字,通过对象的锁机制保证同一时间只有一个线程访问变量。当然,这

个被用作“锁机制”的变量是多个线程共享的。提供一份变量,让不同的线程排队访问。

        3.避免使用实例化变量

        只要在Servlet里面的任何方法里面都不使用实例化变量,那么该Servlet就是线程安全的。

        4.使用ThreadLocal模式

          java.lang.ThreadLocal,提供了一种解决多线程并发问题的方案,该类在维护变量时,实际

使用了当前线程中的一个叫做ThreadLocalMap的独立副本,每个线程可以独立修改属于自己的副

本而不会互相影响,从而隔离了线程和线程,避免了线程访问实例变量发生冲突的问题。

ThreadLocal本身并不是一个变量,而是通过操作当前线程的一个内部变量来达到与其他线程隔离

的目的。
     

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值