我在我的ServletContext中存储了一个HashMap对象。 但多个请求线程正在读取并修改此HashMap。ServletContext对象的线程安全
因为我相信ServletContext对象在请求线程之间共享我需要同步访问这个HashMap吗?或者还有其他更好的方法来达到相同的目的吗?
我在我的ServletContext中存储了一个HashMap对象。 但多个请求线程正在读取并修改此HashMap。ServletContext对象的线程安全
因为我相信ServletContext对象在请求线程之间共享我需要同步访问这个HashMap吗?或者还有其他更好的方法来达到相同的目的吗?
通过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应用程序启动时初始化上下文!
多线程环境中的最佳方式是使用java.util.concurrent.ConcurrentHashMap。 它是专门设计的,允许您在没有任何ConcurrentModificationException
的情况下阅读和修改它。尽管如此,在迭代的情况下,您应该对其实例进行同步,以便始终获得可预测的结果。
如果您通过多线程方式从其中检索其他任何内容,则在上下文中同步会带来很多开销。所以ConcurrentHashMap
是一个更好的解决方案。
你可以阅读一下:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentHashMap.html
的哈希表支持更新检索的完全并发和可调 预期的并发性。检索操作(包括get) 一般不会阻塞,因此可能与更新操作 (包括put和remove)重叠。
您需要同步您的ServletContext对象。为如: -
synchronized(ctx){
//your code here
}
作为一般规则,您不应该使用您不控制的对象的内部(监视器)锁定。 – kbolino
的建议通过@Artem Moskalev
,您可以使用一个ConcurrentHashMap和使用putIfAbsent
方法来存储对象/值,而不是简单的put
方法。
我想在下面添加此评论@Artem Moskalev'
的回答,但我没有足够的声誉。
有一些代表。您可以立即添加评论。 :) –
是的,请确保您修改HashMap
;它是同步的或线程安全的。
你试过了TreeMap吗? – Prateek
你可以把你的代码? –