我正在尝试在ConcurrentHashMap
中的Session
中缓存值。为了避免出现竞争情况并确保在任何线程尝试使用它之前创建我的地图,我使用HttpSessionListener.sessionCreated()
将地图添加到Session
:
@Override
public void sessionCreated(HttpSessionEvent event) {
event.getSession()
.setAttribute(MY_CACHE_KEY, new ConcurrentHashMap());
}
是否可以保证此代码在其他线程访问会话之前完成(例如,通过request.getSession()
)?
我查看了HttpSessionListener
JavaDoc和Servlet 4.0 Spec.,似乎对线程安全性没有任何保证。
Serlvet规格。几次引用会话线程安全性,但据我所知,这些引用均与会话侦听器和会话创建无关:
7.7.1线程问题
执行请求线程的多个servlet可能同时具有对同一会话对象的活动访问权限。容器必须确保操纵代表会话属性的内部数据结构 以线程安全的方式执行。开发人员负责线程安全地访问属性对象本身。这将保护HttpSession对象内部的属性集合免受并发访问, 消除了应用程序导致该集合损坏的机会。除非在规范的其他地方有明确说明(例如,第7-67页的第7.7.1节“线程问题”,用于会话对象),否则必须假定从请求或响应售出的对象是非线程安全的。这包括但不限于从以下位置返回的PrintWriter ServletResponse.getWriter()和从ServletResponse.getOutputStream()返回的OutputStream。
11.5侦听器实例和线程
在开始向应用程序中执行第一个请求之前,需要该容器完成Web应用程序中的侦听器类的实例化。容器必须维护对每个侦听器实例的引用,直到为Web应用程序服务了最后一个请求为止。
对ServletContext和HttpSession对象的属性更改可能同时发生。不需要容器将结果通知同步到属性侦听器类。维护状态的侦听器类负责数据的完整性,并应明确处理这种情况。
很明显,sessionCreated()
必须先完成,线程才能访问会话,但是"obviously correct code" has been unsafe for multithreading before。
ServletContextLister.contextInitialized()
不存在这种歧义,因为可以保证在Servlet
初始化和Servlet.init()
is guaranteed to be single-threaded and occur before any requests之前完成。
我至少已经测试过Tomcat,它可以确保sessionCreated()
在request.getSession()
返回之前完成。我通过在sessionCreated()
中放置一个断点并发送一个名为request.getSession()
的请求来进行测试。在我从断点继续之前,这个请求没有完成。但是,Servlet
容器实现的行为并不能真正证明所有容器/服务器都以这种方式行事。