本文来自stackoverflow的问答,讨论了Java Servlet的工做机制,如何进行实例化、共享变量和多线程处理。html
假设我有一个运行了大量 Servlet
的 web 服务器。经过 Servlet
之间传输信息获得 Servlet
上下文,并设置 session 变量。java
如今,若是有两名或更多使用者向这个服务发送请求,接下来 session 变量会发生什么变化?到底是全部用户都是用共同的变量?仍是不一样的用户使用的变量都不同?若是是后者,服务器如何区分不一样用户?git
另外一个类似的问题,若是有 *n*
名用户访问一个特定的 Servlet
,那么该 Servlet
是仅在第一个用户首次访问的时候实例化,仍是分别为每一个用户实例化?github
当 Servlet 容器(好比 Apache Tomcat)启动后,会部署和加载全部 web 应用。当web 应用被加载,Servlet 容器会建立一次 ServletContext
,而后将其保存在服务器的内存中。web 应用的 web.xml
被解析,找到其中全部 servlet
、filter
和 Listener
或 @WebServlet
、@WebFilter
和 @WebListener
注解的内容,建立一次并保存到服务器的内存中。对于全部过滤器会当即调用 init()
。当 Servlet 容器中止,将卸载全部 web 应用,调用全部初始化的 Servlet 和过滤器的 destroy()
方法,最后回收 ServletContext
和全部 Servlet
、Filter 与 Listener
实例。web
当问题中的 Servlet
配置的 load-on-startup
或者 @WebServlet(loadOnStartup)
设置了一个大于 0 的值,则一样会在启动的时候当即调用 init()
方法。“load-on-startup”中的值表示那些 Servlet 会以相同顺序初始化。若是配置的值相同,会遵循 web.xml
中指定的顺序或@WebServlet
类加载的顺序。另外,若是不设置 “load-on-startup” 值,init()
方法只在第一次 HTTP 请求命中问题中的 Servlet 时才被调用。apache
Servlet 容器附加在一个 web 服务上,这个 web 服务会在某个端口号上监听 HTTP 请求,在开发环境中这个端口一般为 8080,生产环境中一般为 80。当客户端(web 浏览器)发送了一个 HTTP 请求,Servlet 容器会建立新的 HttpServletRequest
和 HttpServletResponse
对象,传递给已建立好而且请求的 URL 匹配 url-pattern
的 Filter
和 Servlet
实例中的方法,全部工做都在同一个线程中处理。api
request 对象能够访问全部该 HTTP 请求中的信息,例如 request header 和 request body。response 对象为你提供须要的控制和发送 HTTP 响应方法,例如设置 header 和 body(一般会带有 JSP 文件中的 HTML 内容)。提交并完成HTTP 响应后,将回收 request 和 response 对象。浏览器
当用户第一次访问该 web 应用时,会经过 request.getSession()
第一次得到 HttpSession。以后 Servlet 容器将会建立 HttpSession
,生成一个惟一的 ID(能够经过 session.getId()
获取)并储存在服务器内存中。而后 Servlet 容器在该次 HTTP 响应的 Set-Cookie
头部设置一个Cookie
,以 JSESSIONID
做为 Cookie 名字,那个惟一的 session ID 做为 Cookie
的值。tomcat
按照 HTTP cookie 规则(正常 web 浏览器和 web 服务端必须遵循的标准),当 cookie 有效时,要求客户端(浏览器)在后续请求的 Cookie
头中返回这个 cookie。使用浏览器内置的 HTTP 流量监控器,你能够查看它们(在 Chrome、Firefox23+、IE9+ 中按 F12,而后查看 Net/Network 标签)。Servlet 容器将会肯定每一个进入的 HTTP 请求的 Cookie
头中是否存在名为JSESSIONID
的 cookie,而后用它的值(session ID)从服务端内存中找到关联的 HttpSession
。安全
你能够在 web.xml
中设置 session-timeout
,默认值为 30 分钟。超时到达以前 HttpSession
会一直存活。因此当客户端再也不访问该 web 应用超过 30 分钟后,Servlet 容器就会回收这个 session。后续每一个请求,即便指定 cookie 名称也不能再访问到相同的 session。Servlet 容器会建立一个新的 Cookie
。
另外一方面,客户端上的 session cookie 有一个默认存活时间,该事件和该浏览器实例运行时间同样长。因此,当客户端关闭该浏览器实例(全部标签和窗口)后,这个 session 就会被客户端回收。新浏览器实例再也不发送与该 session 关联的 cookie。一个新的 request.getSession()
将会返回新的 HttpSession
并设置一个拥有新 session
ID 的 cookie。
ServletContext
与 web 应用存活时间同样长。它被全部 session 中的全部请求共享。HttpServletRequest
和 HttpServletResponse
的存活时间为客户端发送完成到完整的响应(web 页面)到达的这段时间。不会被其余地方共享。全部 Servlet
、Filter
和 Listener
对象在 web 应用运行时都是活跃的。它们被全部 session 中的请求共享。HttpServletRequest
、HttpServletResponse
和 HttpSession
中的全部属性在问题中的对象存活时都会一直保持存活。即使如此,你最关心的多是线程安全。你如今应该学习到 Servlet 和 filter 被全部请求共享。那是 Java 的一个优势,使得多个不一样线程(读取 HTTP 请求)可使用同一个实例。不然为每一个请求从新建立线程的开销实在过于昂贵。
但你应该也意识到永远不要将任何 request 或 session 域中的数据赋值给 servlet 或 filter 的实例变量。它将会被全部其余 session 中的全部请求共享。那是非线程安全的!下面的示例对这种状况进行了展现:
public class ExampleServlet extends HttpServlet { private Object thisIsNOTThreadSafe; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Object thisIsThreadSafe; thisIsNOTThreadSafe = request.setParameter("foo"); // BAD!! Shared among all requests! thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe. } }
原文连接: stackoverflow
首发至: http://www.importnew.com/17025.html,并已同步至 Github,欢迎 Star 关注。