在 HttpSession 内进行同步是否可行?问题:总结答案/解决方案:另请参见

更新:问题后立即解决。

问题:

通常,同步是序列化JVM中的并行请求,例如

private static final Object LOCK = new Object();

public void doSomething() {
  ...
  synchronized(LOCK) {
    ...
  }
  ...
}

在查看Web应用程序时,“JVM全局”范围内的某些同步可能会成为性能瓶颈,并且仅在用户的HttpSession范围内进行同步会更有意义。

以下代码是否可行?我怀疑在会话对象上进行同步是一个好主意,但听到您的想法会很有趣。

HttpSession session = getHttpServletRequest().getSession();
synchronized (session) {
  ...
}

关键问题:
是否保证会话对象对于处理来自同一用户的请求的所有线程都是同一实例?

总结答案/解决方案:

似乎会话对象本身并不总是相同的,因为它依赖于servlet容器(Tomcat,Glassfish等)的实现,并且该方法可能只返回一个包装器实例。getSession()

因此,建议使用存储在会话中的自定义变量作为锁定对象。

这是我的代码建议,欢迎反馈:

在帮助器类中的某个位置,例如:MyHelper

private static final Object LOCK = new Object();

public static Object getSessionLock(HttpServletRequest request, String lockName) {
    if (lockName == null) lockName = "SESSION_LOCK";
    Object result = request.getSession().getAttribute(lockName);
    if (result == null) {
        // only if there is no session-lock object in the session we apply the global lock
        synchronized (LOCK) {
            // as it can be that another thread has updated the session-lock object in the meantime, we have to read it again from the session and create it only if it is not there yet!
            result = request.getSession().getAttribute(lockName);
            if (result == null) {
                result = new Object();
                request.getSession().setAttribute(lockName, result);
            }
        }
    }
    return result;
}

然后你可以使用它:

Object sessionLock = MyHelper.getSessionLock(getRequest(), null);
synchronized (sessionLock) {
  ...
}

对此解决方案有何评论?


答案 1

我在 JavaDoc for WebUtils.getSessionMutex() 中找到了这个很好的解释:

在许多情况下,HttpSession 引用本身也是一个安全的互斥体,因为它将始终是同一活动逻辑会话的相同对象引用。但是,这不能在不同的 servlet 容器中得到保证。唯一100%安全的方法是会话互斥体。

此方法在设置标志时用作锁:synchronizeOnSession

Object mutex = WebUtils.getSessionMutex(session);
synchronized (mutex) {
    return handleRequestInternal(request, response);
}

如果你看一下 的实现,它实际上使用一些自定义会话属性(如果存在(在键下)或实例(如果不是):getSessionMutex()org.springframework.web.util.WebUtils.MUTEXHttpSession

Object mutex = session.getAttribute(SESSION_MUTEX_ATTRIBUTE);
if (mutex == null) {
    mutex = session;
}
return mutex;

回到普通的 servlet 规范 - 100% 确保使用自定义会话属性而不是对象本身。HttpSession

另请参见


答案 2

通常,不要依赖返回相同的对象。无论出于何种原因,servlet 过滤器都很容易围绕会话创建包装器。您的代码将只能看到此包装器,并且它将是每个请求上的不同对象。将一些共享锁放入会话本身。(太可惜了,虽然没有)。HttpServletRequest.getSession()putIfAbsent


推荐