我在我的ServletContext中存储了一个HashMap对象。 但是多个请求线程正在读取和修改此HashMap。
因为我相信ServletContext对象是在请求线程之间共享的,我是否需要同步对此HashMap的访问?或者还有其他更好的方法可以达到目的吗?
答案 0 :(得分:14)
通过ServletContext#setAttribute
发布属性是线程安全的!这可以从Java Servlet规范,第4.5章得出:(...)任何属性绑定
进入上下文可用于属于同一Web的任何其他servlet
应用程序。(...)
(原因:使对象可用于其他servlet也意味着可以将其提供给其他线程。这是唯一可行的,如果使用了正确的同步,则ServletContext#setAttribute
}必须同步。
因此,通过ServletContext#getAttribute
阅读已发布的属性也是如此。
但是当然如果在不同的线程之间共享像HashMap
之类的对象,开发人员必须确保以适当的线程安全方式访问此共享对象本身!使用问题的其他答案中已经说明的ConcurrentHashMap
是一种可能的解决方案,但是在初始化属性时没有解决竞争条件,因为null
检查不是原子的:
ConcurrentMap<String, Object> shared = (...)servletContext.getAttribute("sharedData");
if (shared == null) {
shared = new ConcurrentHashMap<>();
servletContext.setAttribute("sharedData", shared);
}
因此,可以使用ServletContextListener
在Web应用程序启动时初始化上下文!
答案 1 :(得分:3)
由@Artem Moskalev
建议,您可以使用ConcurrentHashMap并使用putIfAbsent
方法来存储对象/值,而不是简单的put
方法。
我想在@Artem Moskalev'
的答案下面添加此评论,但我没有足够的声誉。
答案 2 :(得分:1)
多线程环境中的最佳方法是使用 java.util.concurrent.ConcurrentHashMap 。
它的设计特别允许您在没有任何ConcurrentModificationException
的情况下阅读和修改它。然而,在迭代的情况下,您应该在其实例上进行同步,以便始终获得可预测的结果。
如果以多线程方式从那里检索其他任何内容,则在上下文中进行同步会给您带来很多开销。所以ConcurrentHashMap
是一个更好的解决方案。
你可以在这里阅读:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html
支持检索的完全并发和可调整的哈希表 预期的更新并发性。检索操作(包括获取) 一般不阻塞,因此可能与更新操作重叠 (包括放置和删除)。
答案 3 :(得分:0)
是的,请确保在修改HashMap
时;它是同步的或线程安全的。
答案 4 :(得分:-1)
您需要同步ServletContext对象。例如: -
synchronized(ctx){
//your code here
}