gpt4 book ai didi

java - ServletContext 对象的线程安全

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:02:32 26 4
gpt4 key购买 nike

我正在我的 ServletContext 中存储一个 HashMap 对象。但是多个请求线程正在读取和修改这个 HashMap。

因为我相信 ServletContext 对象在请求线程之间共享,所以我需要同步对此 HashMap 的访问吗?或者有没有其他更好的方法来实现同样的目标?

最佳答案

通过 ServletContext#setAttribute 发布属性是线程安全的!这可以从 Java Servlet 规范第 4.5 章派生:(...) Any attribute bound进入上下文可用于属于同一 Web 的任何其他 servlet应用程序。(...)

(原因:使对象对其他 servlet 可用也意味着使它们对其他线程可用。这只有在使用适当同步的情况下才有可能,因此同步对于实现 ServletContext#setAttribute< 的所有 servlet 容器都是强制性的)。

因此,通过 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 Servlet 规范 中推断出,通过 ServletContext#setAttributeServletContext#getAttribute 在 servlet 之间共享属性确实是 线程安全

但是无论内部如何实现,set/getAttribute 只能保证正确的发布,如果共享属性是一个被修改的可修改对象,它不能保证正确的同步 < em>分享后。这在技术上是不可能的!

例子:

// servlet 1:
Person p = new Person("Keith", "Richards");
context.setAttribute('key', p); // share p
p.setName("Ron", "Wood"); // modification AFTER sharing

// servlet 2 (some time LATER):
Person p = context.getAttribute();
// now, p is guaranteed to be non-null,
// but if class Person is not thread-safe by itself, it may be any of
// - "Keith Richards"
// - "Keith Wood"
// - "Ron Richards"
// - "Ron Wood"
// (depending on the implementation of setName, it may be even worse!)

因此,每个 servlet 上下文属性值必须是

  • 不可变(通过 final 字段)或有效不可变,或者
  • 可变,但在共享后永远不会发生变化,或者
  • 线程安全方式实现(例如同步)

(这适用于 Java 中线程之间共享的所有类型的对象,不仅涉及 servlet 上下文属性)

关于java - ServletContext 对象的线程安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20190070/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com