`
angelbill3
  • 浏览: 256473 次
  • 性别: Icon_minigender_2
  • 来自: 杭州
社区版块
存档分类
最新评论

结合源码谈谈Servlet的实例化、变量以及多线程

阅读更多
1. Servlet官方文档:
http://docs.oracle.com/javaee/7/api/javax/servlet/Servlet.html

javax.servlet包包含了一系列的接口和类,它们共同描述和定义了servlet容器(如Apache tomcat)与servlet类的规范,以及servlet容器运行时的环境。

为什么要学习servlet,因为我们平常熟悉的Tomcat和Spring都与servlet有分不开的关系。Tomcat是著名的servlet容器,Spring的DispatcherServlet就实现了Servlet接口。


2. Servlet版本
Version版本时间JSR Number平台主要改进
Servlet4.0开发中369Java EE 8HTTP/2
Servlet3.12013.05340Java EE 7NIO, WebSocket等。
Servlet3.02009.12315Java EE 6, Java SE 6可插性,加入异步Servlet、安全以及文件上传。
Servlet2.52005.09154Java EE 5, Java SE 5依赖Java SE 5,支持annotation。
Servlet2.42003.11154J2EE1.4, J2SE1.3web.xml使用了XML规范格式。
Servlet2.32001.0853J2EE1.3, J2SE1.2加了Filter相关。
Servlet2.2 1999.08902, 903J2EE1.3, J2SE1.2成为J2EE的一部分,引入war。
Servlet2.11998.11----第一个官方版本,加入RequestDispatcher, ServletContext。
Servlet2.0----JDK 1.1成为Java Servlet开发工具2.0的一部分。
Servlet1.01997.06------



3. 类图
用StarUML画的
3.1 javax.servlet.Servlet相关类图:


3.2 javax.servlet.ServletRequest类图:(ServletResponse略)



4. Servlet生命周期
面试经常被问的问题之一。从3.1的Servlet接口方法中可以看到,Servlet接口定义的方法主要有init(cfg)、service(req, res)、destroy()。
init()、destory()方法只会被调用一次,即在初始化时以及销毁时。
service(req, res)会被调用很多很多次,客户访问web网页,Servlet容器(如Apache Tomcat)会生成一个线程来处理这个用户的请求。这样的设计的优点是节省资源,试想如果客户每请求一次都生成一个新的Servlet,经历init()、service()、destory()生命周期,实在是太浪费资源了。

此外,HttpServlet中的doGet(req, res)或是doPost(req, res)只是对不同的req.getMethod()进行了分类处理,常见的request method有以下几种(摘自HttpServlet类):
    private static final String METHOD_DELETE = "DELETE";
    private static final String METHOD_HEAD = "HEAD";
    private static final String METHOD_GET = "GET";
    private static final String METHOD_OPTIONS = "OPTIONS";
    private static final String METHOD_POST = "POST";
    private static final String METHOD_PUT = "PUT";
    private static final String METHOD_TRACE = "TRACE";



5. 重要的类
5.1 javax.servlet.ServletContext
当servlet容器(如Apache Tomcat)运行时,它会布署、加载所有的web应用。当一个应用被加载时,servlet容器会创建一个ServletContext,然后将它放在内存中(即只有一个实例,不会过期)。接着读取web应用中的web.xml的<servlet>、<filter> (Since Servlet 2.3)以及<listener>,(或是annotation@WebServlet, @WebFilter, @WebListener)配置并实例化它们,将他们放在server的内存中。在创建的时候,init()方法会被调用。

也就是说,
在一个Servlet容器中(single node模式下的话,就是只有一个JVM),可能会有多个Servlet,但只会有一个ServletContext。ServletContext接口定义了一系列的方法,用来给Servlet与容器之间进行“交流”,如返回文件的MIME类型,dispatch requests等。

5.2 HttpServletRequest和HttpServletResponse
javax.servlet中只定义了接口,具体的实现是在Servlet容器中。如运行在Tomcat中的话,具体的装饰者类为:
  • org.apache.catalina.connector.RequestFacada
  • org.apache.catalina.connector.ResponseFacada

当一个用户访问web项目,servlet容器会生成HttpServletResponse、HttpServletResponse,并将它们传递给定义过的Filter链,最终会传给Servlet实例。


6. 多线程
Servlet是线程不安全的。Server在处理用户的request请求通常是以多线程的模式处理,如Tomcat的默认线程池最大数为150。

总结:
  • a. ServletContext实例在web app启动时创建,在web app关闭时销毁。所有的request,session共享同一个ServletContext。
  • b. Servlet, Filter, Listener实例在web app启动时创建,在web app关闭时销毁。所有的request,session共享同一个ServletContext。
  • c. HttpServletRequest和HttpServletResponse起于servlet接收用户的HTTP request,止于response完全返回给用户(网页内容),它们的数据是不会被共享的。

既然容器是以多线程的模式处理request的,那么在Servlet中,在doGet(或doPost等)方法外定义的私有变量可能会有同步问题。在Servlet官方文档也明确提示,并发访问共享资源时要特别小心。共享资源包括内存数据(如类的私有变量,文件,数据库连接等)。

引用官方API:
引用
Servlets typically run on multithreaded servers,so be aware that a servlet must handle concurrent requests and be careful to synchronize access to shared resources. Shared resources include in-memory data such as instance or class variables and external objects such as files, database connections, and network connections.


具体示例:
public class ExampleServlet extends HttpServlet {
    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}



7. 分析Tomcat源码来看Servlet特性
7.1 org.apache.catalina.core.StandardWrapper
Servlet的wrapper类,每个Servlet都有自己的wrapper,负责Servlet的初始化等。
API:http://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/core/StandardWrapper.html

部分源码:
public class StandardWrapper extends ContainerBase
    implements ServletConfig, Wrapper, NotificationEmitter {
    
    protected volatile Servlet instance = null;
    protected volatile boolean singleThreadModel = false;

    @Override
    public synchronized void load() throws ServletException {
        instance = loadServlet();

        if (!instanceInitialized) {
            initServlet(instance);
        }
    // 略
    }

    private synchronized void initServlet(Servlet servlet)
            throws ServletException {

        if (instanceInitialized && !singleThreadModel) return;
    // 略
        servlet.init(facade);
    }
}

变量singleThreadModel默认值为false,即默认情况下是多线程模式。load()方法中也能看到先是对instance进行实例化,接着判断是否是多线程模式,如果是,那么进行initServlet,调用initServiet(servlet),再进行一系列的操作后,最后调用servlet.init(cfg)方法,也就是3.1类图中标出的(也是第4节生命周期中说到的)init方法。

由此可见,若已经被实例化过(instanceInitialized),那么代码就直接return了,并不会进行servlet.init(cfg),也就是说这个方法只会被调用一次。即servlet生命周期的奥义。

7.2 org.apache.tomcat.util.net.NioEndpoint
如果要看Tomcat的线程池实现,可以看NioEndpoint类。此类继承了抽象类AbstractEndpoint<S>,其它的实现还有AprEndpoint, JIoEndpoint, Nio2Endpoint。
具体API戳:[url]http://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/tomcat/util/net/AbstractEndpoint.html [/url]


参考:
Servlet维基百科:https://en.wikipedia.org/wiki/Java_servlet
Tomcat官方API:http://tomcat.apache.org/tomcat-8.0-doc/api/overview-summary.html
Tomcat在线源码(用Maven下载下来更方便):http://grepcode.com/snapshot/repo1.maven.org/maven2/org.apache.tomcat/tomcat-catalina/8.0.24/
文章:https://blogs.oracle.com/arungupta/entry/what_s_new_in_servlet
文章:http://www.tuicool.com/articles/AZb2ai
在线讨论:http://stackoverflow.com/questions/3106452/how-do-servlets-work-instantiation-sessions-shared-variables-and-multithreadi
分享到:
评论

相关推荐

    1工作临时-servlet 多线程问题

    1. **Servlet实例化策略**:使用Per-Thread(每个线程一个实例)模型,或者采用Prototype(原型)模式,让容器为每个请求创建一个新的Servlet实例,但这会增加内存开销。 2. **线程安全编程**:对于必须共享的成员...

    servlet源码

    `init()`方法在Servlet实例化后被调用,`service()`方法在接收到请求时调用,`destroy()`方法在Servlet卸载前调用。 5. **Servlet配置**:在`web.xml`部署描述符中,我们可以通过`&lt;servlet&gt;`和`&lt;servlet-mapping&gt;`...

    Servlet注意点

    1. **线程安全**:由于Servlet默认是多线程的,开发者需要注意同步问题,避免在Servlet中使用全局变量或不线程安全的对象。 2. **性能优化**:可以通过实现`SingleThreadModel`接口使每个请求都创建新的Servlet实例...

    servlet 源码

    7. **多线程模型**: 由于Servlet容器会复用Servlet实例来处理多个并发请求,因此开发者需要注意线程安全问题。在编写Servlet时,应避免在实例变量中存储请求特定的数据。 8. **URL映射**: 在Web应用的部署描述...

    韩顺平servlet部分源码

    6. **多线程与性能**: Servlet默认是线程安全的,因为每个请求都会创建一个新的线程来调用Servlet。但是,开发者需要注意避免在Servlet中使用全局变量或不安全的数据结构,以防止线程安全问题。 7. **过滤器...

    servlet源码文件

    7. **多线程安全问题**: 由于Servlet容器可能会复用Servlet实例,所以开发者需要注意线程安全问题。对于共享的成员变量,需要使用同步机制或者使用无状态的Servlet设计。 8. **过滤器Filter**: 除了Servlet外,...

    Servlet进阶的相关内容

    3. **多线程安全**:Servlet默认是线程安全的,意味着服务器可能会并发地调用同一个Servlet实例的`service()`方法。因此,开发者需要确保在Servlet中不使用非线程安全的成员变量。 4. **Servlet容器**:如Tomcat,...

    韩顺平servlet笔记.rar下载

    9. **多线程**:Servlet默认是线程安全的,一个Servlet实例可以服务于多个并发请求。开发者需要注意同步问题,避免在Servlet中使用静态变量存储请求相关数据。 10. **过滤器(Filter)**:Servlet API还提供了过滤...

    web笔记 —— Servlet

    由于Servlet是多线程的,同一个Servlet实例可能会服务于多个请求。因此,开发者需要注意线程安全问题,避免在Servlet中使用全局变量。 七、Servlet的会话管理 通过HttpServletRequest的getSession()方法,Servlet...

    servlet和struts2笔记

    - 由于一个 Servlet 实例可能会同时处理多个请求,因此在 Servlet 中声明成员变量存储用户数据可能导致线程安全问题。 - **解决方案**: - 实现 `SingleThreadModel` 接口,但这种方式已被废弃,因为效率低下。 ...

    servlet-src

    7. 多线程与并发:Servlet容器会为每个请求创建一个新的线程来调用Servlet,因此Servlet需要考虑线程安全问题,特别是在共享数据或状态时。 8. Servlet容器:如Tomcat、Jetty等,它们负责管理Servlet的生命周期,...

    servlet学习

    7. **多线程模型**: 每个Servlet实例可能会被多个请求线程同时调用,因此Servlet需要处理线程安全问题。开发者需要确保Servlet中的状态变量正确地同步。 8. **过滤器(Filter)**: 过滤器可以在请求到达Servlet...

    Java高手真经(网络开发卷):JavaWeb核心技术.servlet+javabean源码

    以及利用Servlet的多线程特性,确保并发处理的效率。 总之,理解并熟练掌握Servlet和JavaBean是成为一名合格的JavaWeb开发者的必经之路。通过实际项目的练习和不断学习,你可以深化对这些技术的理解,提升自己的...

    serlvet 源码

    5. **多线程模型**:Servlet默认是线程安全的,同一个Servlet实例可能服务于多个并发请求。开发者需要注意避免在Servlet中使用非线程安全的变量。 6. **URL映射**:在web.xml配置文件中,我们可以通过`&lt;servlet-...

    java核心技术 corejava 第八版 所有配套源码 经典实例

    5. **多线程**:详细阐述了Java中线程的创建、同步、互斥和通信,包括Thread类和Runnable接口,以及synchronized关键字、wait/notify机制和并发工具类的使用。 6. **输入/输出流**:讲解了I/O流的基本概念,包括...

    tomcat7.070 源码及转成eclipse

    7. **线程模型**:Tomcat采用多线程模型处理请求,`Executor`接口及其实现允许自定义线程池,以优化并发性能。 8. **连接器与协议处理器**:Tomcat支持多种连接器,如基于NIO的` CoyoteNioProtocol`,基于APR的`...

    servlet源代码

    6. **多线程与并发**:Servlet容器通常会为每个请求创建一个新的线程,因此开发者需要注意线程安全问题,避免在Servlet中使用不安全的全局变量。 7. **异步Servlet**:Java Servlet 3.0及以上版本引入了异步Servlet...

    java源码网上购物系统(JavaBean+Servlet+jsp)

    - **init()**:初始化方法,在Servlet实例创建时调用一次。 - **service()**:处理请求的方法,根据请求类型(GET/POST)选择适当的方法执行。 - **destroy()**:销毁方法,当Servlet不再需要时被调用。 - **...

Global site tag (gtag.js) - Google Analytics