1

所有,为了更好地理解线程问题。我为它写了一些测试代码,我认为下面的代码应该是线程安全的方式AttributesHttpSession

HttpSession session = request.getSession();
synchronized (session) {
  Integer n = (Integer) session.getAttribute("foo");
  session.setAttribute("foo", (n == null) ? 1 : n + 1);
}  

但实际上它的答案告诉我它不是。我只是无法理解,在我看来,我认为会话是一个客户端和服务器之间的转换。这种情况有线程问题吗?如果有,请告诉我在哪种情况下此代码不是线程安全的。谢谢。

4

1 回答 1

1

AFAIK,没有什么能阻止 servlet 容器在每次线程请求会话时返回不同的对象(即真实的实际会话的代理)。

如果容器这样做,则在会话上同步将没有任何帮助,因为每个线程将在不同的对象上同步。

拥有线程安全计数器的最简单方法是使用 AtomicInteger,并调用其增量方法之一,但这不会阻止两个并发线程第一次存储 AtomicInteger,如果它们都将其视为 null。

最简单的确定方法(尽管可能不是最快的)是使用全局锁来获取属性:

public static synchronized AtomicInteger getCounterFromSession(HttpSession session) {
    AtomicInteger counter = (AtomicInteger) session.getAttribute("counter");
    if (counter == null) {
        counter = new AtomicInteger();
        session.setAttribute("counter", counter);
    }
    return counter;
}

也就是说,在集群应用程序中,会话是持久的或跨集群节点复制的,因此这也不会带来任何保证。将计数器存储在数据库中将是这里的解决方案。

于 2013-05-04T12:09:24.457 回答