Servlet的工作原理+Servlet的生命周期+Servlet线程安全

Servlet的工作原理

首先客户发送一个请求,Servlet是调用service()方法对请求进行响应的,通过源代码可见,service()方法中对请求的方式进行了匹配,选择调用doGet,doPost等这些方法,然后再进入对应的方法中调用逻辑层的方法,实现对客户的响应。在Servlet接口和GenericServlet中是没doGet,doPost等等这些方法的,HttpServlet中定义了这些方法,但是都是返回error信息,所以,我们每次定义一个Servlet的时候,都必须实现doGet或doPost等这些方法。

每一个自定义的Servlet都必须实现Servlet的接口,Servlet接口中定义了五个方法,其中比较重要的三个方法涉及到Servlet的生命周期,分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的,不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet,因此HttpServlet也实现了Servlet接口。所以我们定义一个Servlet的时候只需要继承HttpServlet即可。
Servlet接口和GenericServlet是不特定于任何协议的,而HttpServlet是特定于HTTP协议的类,所以HttpServlet中实现了service()方法,并将请求ServletRequest,ServletResponse强转为HttpRequest和HttpResponse。

Servlet的生命周期

Servlet的生命周期包括三个阶段:一是容器初始化init(),二是调用service方法,判断客户端请求的方式。三是销毁,调用destroy()方法。

详细的 Servlet 生命周期示意图如下:

Servlet的工作原理+Servlet的生命周期+Servlet线程安全_第1张图片

Servlet线程安全

在Servlet整个生命周期中是由Tomcat来维护的,当客户端第一次发起请求的时候,会根据web.xml文件中的配置实例化一个Servlet,而在以后客户端的每一次请求都会使用该实例来处理后续的工作,直到Tomcat停止该项目,这个Servlet才会被销毁,所占用的资源才会释放。
当客户端发来多个请求的时候,Servlet将采用多线程来解决这样的并发,而在Tomcat本身也维护了一个线程池来处理并发。线程池实际上是等待执行代码的一组线程叫做工作组线程(Worker Thread),Tomcat容器使用一个调度线程来管理工作组线程(Dispatcher Thead)。
Servlet线程安全问题很大部分是由实例变量造成的,只要在Servlet里面的任何方法里面都不使用实例变量,那么该Servlet就是线程安全的。

有什么方法能够解决这个问题呢?

方案是将实例变量修改为局部变量:

多线程下每个线程对局部变量都会有自己的一份copy,这样对局部变量的修改只会影响到自己的copy而不会对别的线程产生影响,线程安全的。

Java 内存模型中,方法中的临时变量是在栈上分配空间,方法的执行就是不断的压栈出栈的过程,进程中的所有线程共享进程的内存空间,而且每个线程都有自己私有的栈空间,所以它们不会影响线程的安全。

但是对于实例变量来说,由于servlet在Tomcat中是以单例模式存在的,所有的线程共享实例变量。多个线程对共享资源的访问就造成了线程不安全问题。

另外一种方案ThreadLocal:

ThreadLocal概念:线程局部变量,是一种多线程间并发访问变量的解决方案。它完全不提供锁,而是使用空间换时间的手段,为每个线程提供变量的独立副本,以保障线程安全。从性能上来说,ThreadLocal不具有绝对的优势,在并发不是很高的情况下,加锁的性能会更好,但是作为一套与锁完全无关的线程安全解决方案,在高并发量或者竞争激烈的场景,使用ThreadLocal可以在一定程度上减少锁竞争。具体可参考文章《使用wait/notify模拟Queue+ThreadLocal》

 

转载自:https://blog.csdn.net/qq_27828675/article/details/80566319

你可能感兴趣的:(Java)