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