如何servlet的工作?实例化,会话,共享变量和多线程

问题描述 投票:1066回答:8

假设,我有拥有众多的servlet的Web服务器。对于那些servlet的我设置会话和实例变量之间传递信息。

现在,如果2个或更多用户发送请求到该服务器,然后会发生什么变化会话变量?他们都将共同为所有用户或他们将是为每个用户不同。如果它们是不同的,那么如何能够在服务器为不同的用户之间进行区分?

还有一个类似的问题,如果有n用户访问特定的servlet,然后这个servlet实例化得到仅在第一次的第一个用户访问,或是否得到单独实例化的所有用户?换句话说,会发生什么情况的实例变量?

java multithreading servlets session-variables instance-variables
8个回答
1765
投票

ServletContext中

当servlet容器(如Apache Tomcat)启动时,它会部署和装载它的所有Web应用程序。当一个Web应用程序被加载,servlet容器创建ServletContext一次,并保持它在服务器的内存中。 Web应用程序的web.xml并全部纳入web-fragment.xml文件的解析,每个<servlet><filter><listener>发现(或分别@WebServlet@WebFilter@WebListener注释每个类)被实例化一次,并保存在服务器的内存。对于每一个实例化的过滤器,其init()方法被调用一个新的FilterConfig

Servlet具有<servlet><load-on-startup>@WebServlet(loadOnStartup)值大于0大,则其init()方法也与新ServletConfig启动期间调用。这些小服务程序在由该值指定的相同顺序进行初始化(1是第一,2是第二,等等)。如果多于一个的servlet指定了相同的值,则每个这些小服务程序的如它们出现在web.xmlweb-fragment.xml,或@WebServlet类加载装入相同的顺序。在该事件的“负载上启动”值不存在,每当HTTP请求命中那个servlet的非常第一次init()方法将被调用。

当servlet容器与所有的上述初始化步骤完成,然后,ServletContextListener#contextInitialized()将被调用。

当servlet容器关闭时,它卸载所有Web应用程序,调用其全部初始化servlet和过滤器的方法destroy(),所有ServletContextServletFilterListener实例丢弃。最后,ServletContextListener#contextDestroyed()将被调用。

HttpServletRequest和HttpServletResponse的

servlet容器附连到该用于在某些端口号的HTTP请求侦听(端口8080通常在生产发展和端口80期间使用)的web服务器。当客户端(与web浏览器programmatically using URLConnection例如用户,或)发送HTTP请求,servlet容器创建新HttpServletRequestHttpServletResponse对象和通过链中的任何定义Filter传递它们,并最终的Servlet实例。

filters的情况下,该方法doFilter()被调用。当servlet容器的代码调用chain.doFilter(request, response),请求和响应上继续下一个过滤器,或打的servlet,如果没有剩余的过滤器。

servlets的情况下,该方法service()被调用。默认情况下,该方法确定哪个的doXxx()方法之一来调用基于关request.getMethod()的。如果所确定的方法是从servlet不存在,然后在响应中返回一个HTTP 405错误。

请求对象可以访问所有的有关HTTP请求时,信息,例如其URL,标头,查询字符串和身体。响应对象提供了控制并发送HTTP响应你想要的方式,例如,允许您设置的头和身体(通常从一个JSP文件生成的HTML内容)的能力。当HTTP响应被提交和成品,请求和响应对象被再循环和再利用提供。

HttpSession中

当客户端访问的web应用程序的第一次和/或用于经由HttpSession首次获得request.getSession(),servlet容器创建一个新的HttpSession对象,生成一个长的和独特的ID(其可以通过session.getId()获得),并存储它在服务器的内存中。 servlet容器还设置与Cookie正如它的名字和作为其值的唯一会话ID的HTTP响应的报头Set-Cookie一个JSESSIONID

由于每HTTP cookie specification(合同任何像样的Web浏览器和Web服务器必须坚持),客户端(网页浏览器)需要这个cookie发送回在后续请求中Cookie头,只要cookie有效期(即唯一的ID必须是指未到期的会话和域和路径是正确的)。使用浏览器内置的HTTP流量监控,可以验证cookie有效期(在Chrome / Firefox中23+ / IE9 +按F12,并检查网络/网络选项卡)。 servlet容器将检查是否与名称Cookie cookie的存在每个传入HTTP请求的报头JSESSIONID并使用其值(会话ID),以获得从服务器的存储器相关联的HttpSession

所述HttpSession保持活着,直到它已经空闲(即在请求未使用)为比在指定<session-timeout>的超时值,在web.xml设置更多。超时值默认为30分钟。所以,当客户端不访问网络应用程序超过规定的时间越长,servlet容器象垃圾一样清除会话。每个后续请求,即使指定的cookie,将无法获得相同的会话了; servlet容器将创建一个新的会话。

在客户端,会话cookie保持活着,只要浏览器实例运行。因此,如果客户关闭浏览器实例(所有标签/窗口),那么会话丢弃在客户的身边。在一个新的浏览器实例,与会话相关的Cookie也就不存在了,所以将不再发送。这将导致一个全新的HttpSession被创建,与正在使用一个全新的会话cookie。

简而言之

  • ServletContext生活,只要Web应用程序生命。它在所有会话的所有请求共享。
  • HttpSession生活,只要客户端与同一个浏览器实例中的Web应用程序进行交互,和会话未在服务器端超时。它是在同一个会话的所有请求共享。
  • HttpServletRequestHttpServletResponse生活从servlet接收来自客户端的HTTP请求的时间,直到完全缓解(网页)已经到来。这是其他地方没有共享。
  • 所有ServletFilterListener情况下,只要Web应用程序生命居住。他们在所有会话的所有请求共享。
  • 只要是在attributeServletContextHttpServletRequest定义的任何HttpSession将生活中的问题生活中的对象。对象本身代表了豆管理框架如JSF,CDI,弹簧等“范围”那些框架存储其作用域bean作为其最接近的匹配范围的attribute

线程安全

这就是说,你的主要问题可能是线程安全的。你现在应该知道,servlet和过滤器的所有请求之间共享。这是关于Java的好处,它的多线程和不同的线程(读:HTTP请求)可以使用相同的实例。这否则会过于昂贵重建,init()destroy()他们为每一个请求。

你也应该意识到,你永远不应该将任何请求或会话范围的数据作为一个servlet或过滤器的实例变量。它会在其他会话中所有其他请求之间共享。这不是线程安全的!下面的例子说明了这一点:

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.
    } 
}

See also:


421
投票

会议

总之:Web服务器发出一个唯一的标识符,以他的首次访问每个访问者。访问者必须带回ID为他围绕公认的下一次。该标识符还允许服务器正确通过隔离针对的另一个会话拥有的对象。

Servlet的实例化

如果时加载的启动是假的:

如果时加载的启动是正确的:

一旦他的服务模式,并在凹槽中,同一个servlet将来自所有其他客户端的请求工作。

为什么不是一个好主意,有每个客户端的一个实例?想想这个:你会聘请为每来一个订单比萨饼的家伙?做到这一点,你会被市场淘汰的在任何时间。

它配备了一个小的风险,但。请记住:这种单人拥有他的口袋里所有的订单信息:所以,如果你不小心thread safety on servlets,他可能最终给了错误的为了某个客户。


42
投票

会议在Java servlet为相同的其他语言如PHP会议。这是唯一的用户。该服务器可以跟踪它以不同的方式,如饼干,URL重写等等。这Java doc文章解释了它在Java servlet的上下文,并表示会究竟是如何保持是留给服务器的设计实现细节。该规范只规定,它必须保持为唯一跨多个连接的用户到服务器。退房this article from Oracle约两个你的问题的更多信息。

编辑有关于如何使用会话的servlet里面工作的优秀教程here。而here是来自Sun关于Java Servlet的,它们是什么,以及如何使用它们的一章。这两个条款之间,你应该能够回答您的所有问题。


33
投票

当servlet容器(Apache Tomcat等)启动时,它会从web.xml文件(每个应用程序只有一个),如果出现任何错误或显示了在容器侧控制台的错误,否则读,它将部署和加载所有网页通过web.xml中的应用程序(因此将其命名为部署描述符)。

在这个servlet实例化阶段,servlet实例已准备就绪,但因为它是与两个信息丢失不能服务于客户端请求: 1:上下文信息 2:初始配置信息

servlet引擎创建的ServletConfig接口对象封装上述缺失的信息到它servlet引擎通过作为一个参数供给ServletConfig对象的引用调用的servlet的init()。一旦的init()被完全执行的servlet是准备服务的客户机请求。

Q) In the lifetime of servlet how many times instantiation and initialization happens ??

A)只有一次(为每个客户端请求创建一个新的线程)只有一个的servlet实例提供任何数量的客户端请求即,服务一个客户端请求的服务器不会死了。它等待其他客户端请求,即什么样的CGI(用于创建一个新的过程中,每客户端请求)的限制被克服和servlet(内部servlet引擎创建线程)。

Q)How session concept works?

A)每当的getSession()被调用HttpServletRequest对象上

第1步:用于传入会话ID请求对象进行评价。

步骤2:如果创建ID不可用一个全新的HttpSession对象,并产生其相应的会话ID的会话ID(哈希表的IE)被存储到的HttpServlet响应对象和HttpSession对象的引用返回给servlet(的doGet / doPost方法) 。

第3步:如果没有创建ID提供全新的会话对象的会话ID从请求对象中搜索回升是会话的集合中使用会话ID作为重点提出。

一旦搜索成功会话ID被存储到的HttpServletResponse和所述现有会话的对象引用被返回给UserDefineservlet的的doGet()或doPost()。

Note:

1)当控制叶片从servlet代码到客户端不要忘记,会话对象是正在举行的servlet容器即servlet引擎

2)多线程留给Servlet开发人员实现即,处理的客户没有任何的多个请求操心多线程代码

Inshort form:

当应用程序开始(它被部署servlet容器上),或者当它第一次访问(取决于负载上启动设置)时,小服务程序被实例化时,servlet的init()方法被调用时创建一个servlet然后servlet(它的唯一实例)处理所有的请求(其service()方法被多个线程调用)。这就是为什么它是不可取的有它的任何同步,你应该避免的servlet实例变量当应用被卸载(servlet容器停止)时,destroy()方法被调用。


20
投票

会话 - 什么克里斯汤普森说。

实例 - 当容器接收被映射到servlet第一请求(除非该servlet被配置成在启动时加载在<load-on-startup>web.xml元件)一个servlet被实例化。同样的实例用于提供后续的请求。


13
投票

Servlet规范JSR-315清楚地定义Web容器行为在服务(和的doGet,doPost方法,doPut等)的方法(2.3.3.1多线程问题,第9页):

一个servlet容器可以通过servlet的服务方法发送的并发请求。为了处理请求,这个Servlet开发者必须为在服务方法多线程并发处理的适当规定。

虽然不建议,为开发一种替代方法是实现其要求的容器,以保证只有一个在服务方法的时候请求线程SingleThreadModel接口。一个servlet容器可以,或者通过维持servlet的实例池满足通过序列上的servlet请求这一要求。如果servlet是已被标识为分布式Web应用程序的一部分,容器可保持Servlet实例的应用程序是分布在每个JVM池。

对servlet不执行SingleThreadModel接口,如果服务方法(或方法,诸如的doGet或doPost其分派到的HttpServlet抽象类的服务方法)已经与同步关键字定义,servlet容器不能使用实例池的方法,但必须序列通过它的请求。强烈建议开发人员不同步的服务方法(或方法分派给它)在这种情况下,因为对性能产生不利影响


0
投票

如从上述解释清楚,通过实现了SingleThreadModel,一个servlet可以由servlet容器保证线程安全。容器的实现可以通过两种方式做到这一点:

1)序列化请求(排队)到单个实例 - 这个类似于NOT实现SingleThreadModel BUT同步服务/方法的doXXX一个servlet;要么

2)创建实例池 - 这是一个更好的选择,并作为对托管的servlet环境的限制性参数(内存/ CPU时间)在servlet的开机/初始化工作/时间之间的权衡。


-1
投票

第Servlet都不线程安全

这允许同时接入多个线程

如果u想使它servlet作为线程安全的。,U可以去

Implement SingleThreadInterface(i)这是一个空白接口有没有

方法

或者我们可以去同步方法

我们可以把整个服务方法,通过使用同步的同步

关键字的方法的前面

例::

public Synchronized class service(ServletRequest request,ServletResponse response)throws ServletException,IOException

或者我们可以将代码中的synchronized块看跌块

例::

Synchronized(Object)

{

----Instructions-----

}

我觉得synchronized块比使得整个方法好

同步

© www.soinside.com 2019 - 2024. All rights reserved.