Is synchronization within an HttpSession feasible?

前端 未结 9 706
再見小時候
再見小時候 2020-12-04 14:47

UPDATE: Solution right after question.

Question:

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

相关标签:
9条回答
  • 2020-12-04 15:02

    Personally, I implement session-locking with the help of an HttpSessionListener*:

    package com.example;
    
    @WebListener
    public final class SessionMutex implements HttpSessionListener {
        /**
         * HttpSession attribute name for the session mutex object.  The target for 
         * this attribute in an HttpSession should never be altered after creation!
         */
        private static final String SESSION_MUTEX = "com.example.SessionMutex.SESSION_MUTEX";
    
        public static Object getMutex(HttpSession session) {
            // NOTE:  We cannot create the mutex object if it is absent from  
            // the session in this method without locking on a global 
            // constant, as two concurrent calls to this method may then 
            // return two different objects!  
            //
            // To avoid having to lock on a global even just once, the mutex 
            // object is instead created when the session is created in the 
            // sessionCreated method, below.
    
            Object mutex = session.getAttribute(SESSION_MUTEX);
    
            // A paranoia check here to ensure we never return a non-null 
            // value.  Theoretically, SESSION_MUTEX should always be set, 
            // but some evil external code might unset it:
            if (mutex == null) {
                // sync on a constant to protect against concurrent calls to 
                // this method
                synchronized (SESSION_MUTEX) { 
                    // mutex might have since been set in another thread 
                    // whilst this one was waiting for sync on SESSION_MUTEX
                    // so double-check it is still null:
                    mutex = session.getAttribute(SESSION_MUTEX);
                    if (mutex == null) {
                        mutex = new Object();
                        session.setAttribute(SESSION_MUTEX, mutex);
                    }
                }
            }
            return mutex; 
        }
    
        @Override
        public void sessionCreated(HttpSessionEvent hse) {
            hse.getSession().setAttribute(SESSION_MUTEX, new Object());
        }
    
        @Override
        public void sessionDestroyed(HttpSessionEvent hse) {
            // no-op
        }
    }
    

    When I need a session mutex, I can then use:

    synchronized (SessionMutex.getMutex(request.getSession())) {
        // ...
    }
    

    __

    *FWIW, I really like the solution proposed in the question itself, as it provides for named session locks so that requests for independent resources don't need to share the same session lock. But if a single session lock is what you want, then this answer might be right up your street.

    0 讨论(0)
  • 2020-12-04 15:04

    Synchronization occurs when a lock is placed on an object reference, so that threads that reference the same object will treat any synchronization on that shared object as a toll gate.

    So, what your question raises an interesting point: Does the HttpSession object in two separate web calls from the same session end up as the same object reference in the web container, or are they two objects that just happen to have similar data in them? I found this interesting discussion on stateful web apps which discusses HttpSession somewhat. Also, there is this discussion at CodeRanch about thread safety in HttpSession.

    From those discussions, it seems like the HttpSession is indeed the same object. One easy test would be to write a simple servlet, look at the HttpServletRequest.getSession(), and see if it references the same session object on multiple calls. If it does, then I think your theory is sound and you could use it to sync between user calls.

    0 讨论(0)
  • 2020-12-04 15:07

    I found this nice explanation in spring-mvc 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

    • http://www.theserverside.com/discussions/thread.tss?thread_id=42912
    0 讨论(0)
  • 2020-12-04 15:07

    Using

    private static final Object LOCK = new Object();
    

    you are using the same lock for all sessions and it was the core reason for deadlock I did face. So every session in your implementation has the same race condition, which is bad.

    It needs change.

    Other suggested answer:

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

    seems much better.

    0 讨论(0)
  • 2020-12-04 15:08

    Another solution suggested in "Murach's Java Servlets and JSP (3rd Edition)" book:

    Cart cart;
    final Object lock = request.getSession().getId().intern();
    synchronized (lock) {
        cart = (Cart) session.getAttribute("cart");
    }
    
    0 讨论(0)
  • 2020-12-04 15:17

    The answers are correct. If you want to avoid the same user executes 2 different (or the same) requests at the same time, you can synchronize on the HttpSession. The best to do this is to use a Filter.

    Notes:

    • if your resources (images, scripts, and any non-dynamic file) also comes through the servlet, you could create a bottleneck. Then be sure, the synchonization is only done on dynamic pages.
    • Try to avoid the getSession directly, you should better test if the session already exists because a session is not automatically created for guests (as nothing has to be stored in the session). Then, if you call getSession(), the session will be created and memory will be lost. Then use getSession(false) and try to deal with the null result if no session already exists (in this case, don't synchronize).
    0 讨论(0)
提交回复
热议问题