Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

UPDATE: Solution right after question.

Question:

Usually, synchronization is serializing parallel requests within a JVM, e.g.

private static final Object LOCK = new Object();

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

When looking at web applications, some synchronization on "JVM global" scope is maybe becoming a performance bottleneck and synchronization only within the scope of the user's HttpSession would make more sense.

Is the following code a possibility? I doubt that synchronizing on the session object is a good idea but it would be interesting to hear your thoughts.

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

Key Question:
Is it guaranteed that the session object is the same instance for all threads processing requests from the same user?

Summarized answer / solution:

It seems that the session object itself is not always the same as it depends on the implementation of the servlet container (Tomcat, Glassfish, ...) and the getSession() method might return just a wrapper instance.

So it is recommended to use a custom variable stored in the session to be used as locking object.

Here is my code proposal, feedback is welcome:

somewhere in a Helper Class, e.g. 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;
}

and then you can use it:

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

Any comments on this solution?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
148 views
Welcome To Ask or Share your Answers For Others

1 Answer

I found this nice explanation in JavaDoc for WebUtils.getSessionMutex():

In many cases, the HttpSession reference itself is a safe mutex as well, since it will always be the same object reference for the same active logical session. However, this is not guaranteed across different servlet containers; the only 100% safe way is a session mutex.

This method is used as a lock when synchronizeOnSession flag is set:

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

If you look at the implementation of getSessionMutex(), it actually uses some custom session attribute if present (under org.springframework.web.util.WebUtils.MUTEX key) or HttpSession instance if not:

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

Back to plain servlet spec - to be 100% sure use custom session attribute rather than HttpSession object itself.

See also


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...