Is Httpsession Thread Safe, Are Set/Get Attribute Thread Safe Operations

is @Autowired HttpSession thread safe in springmvc?

What you get wired from Spring is just the HttpSession. There are no special thread safety guarantees.

Implementations using HttpSession are not necessarily thread safe. See also this question: Is HttpSession thread safe, are set/get Attribute thread safe operations?

You could reduce the chances of race conditions causing problems without synchronization by:

  • reducing the chance that no two requests will be processed simultaneously for the session
  • not messing with the session from another thread than the request thread

If you need to process data from the session asynchronously, I find it is a better strategy to pull immutable objects from the session in the request thread and then use those in asynchronous processes on the server. That way you avoid access from the session as much as possible. That said, if you need to be totally safe (why take a risk), you need to synchronize.

synchronized(mutexFor(session)) {
final String data = session.getAttribute("data");
//do some work
session.setAttribute("data", newValue);
}

If this is not thread safe in what ways can I make it thread safe?

The posts you're referencing are telling you that the HTTPSession is shared state and you have to be careful when multiple threads can modify it. Here the opportunity for threads to interfere is if somehow you have two concurrent POSTs from the same user that call this method. You can handle it by something like

private void updateSessionHashSet(HttpServletRequest req){
String csv = req.getParameter("theCsv");
Set<String> hs = new HashSet<String>();
String[] arr = csv.split(",");
for(int i = 0; i < arr.length; i++){
hs.add(arr[i].trim());
}
req.getSession().setAttribute("theHashSet", hs);
}

This way you put together the new hashset in a local variable first, then the code overwrites the reference to the session's theHashSet attribute atomically. If two threads are calling the same code then one or the other will win, but the two won't be jumbled together (which it seems like could happen with your posted code).

Alternatively you could so like some of the answers on the linked posts advise and synchronize on the HttpSession object, but since you're synchronizing on a Java object that is local to that instance of the application, you have to look out for the case where the application is distributed across several nodes. Synchronization means using a shared lock, and across JVMs there's no sharing.

It's really common for IT departments to want to deploy an application to several instances to avoid having a single-point-of-failure. You can deal with the synchronization problem by pinning the session to a specific server, but that makes load-balancing hard.

By the way stashing stuff in the HttpSession probably isn't the best plan, you should save this in some kind of data store. The best way to have safe concurrency is to minimize the amount of mutable state. The HttpSession is not something to use as long-term storage.

For guidelines about thread-safety of Java EE stuff, I have a related answer here.

Understanding Goetz's article on Thread safety of HttpSession

Your code is still not Thread-safe:

ShoppingCart cart = cartRef.get();
if (cart == null) {
cart = new ShoppingCart(...);
session.setAttribute("shoppingCart",
new AtomicReference<ShoppingCart>(cart));
}

This is because two Threads can both get a cart of null, create new shopping cart objects, and insert them into the session. One of them will "win," meaning one will set the object used by future requests, but the other will -- for this request -- use a totally different cart object.

To make this Thread-safe, you would need to do something like this, following the idiom from the article you referenced:

while (true) {
ShoppingCart cart = cartRef.get();
if (cart != null) {
break;
}
cart = new ShoppingCart(...);
if (cartRef.compareAndSet(null, cart))
break;
}

With the above code, if two Threads using the same HttpSession enter the while loop at the same time, there is no data race that can cause them to use different cart objects.

To address the part of the problem that Brian Goetz doesn't address in the article, namely how do you get the AtomicReference into the session in the first place, there's an easy and probably (but not guaranteed) thread-safe way to do this. Namely, implement a session listener and put the empty AtomicReference objects into the session in its sessionCreated method:

public class SessionInitializer implements HttpSessionListener {
public void sessionCreated(HttpSessionEvent event){
HttpSession session = event.getSession();
session.setAttribute("shoppingCart", new AtomicReference<ShoppingCart>());
}
public void sessionDestroyed(HttpSessionEvent event){
// No special action needed
}
}

This method will be called once for each session, only when it is created, so this is an appropriate place to do any initialization that is needed for a session. Unfortunately, the Servlet spec does not require that there is a happens-Before relationship between calling sessionCreated() in your listener and calling your service() method. So this is apparently not guaranteed to be thread safe, and can potentially vary in behavior between different Servlet containers.

Thus, if there is even a small chance that a given session can have more than one request in flight at a time, this is not safe enough. Ultimately, in this case, you need to use a lock of some sort to initialize the session. You could do something like this:

HttpSession session = request.getSession(true);
AtomicReference<ShoppingCart> cartRef;
// Ensure that the session is initialized
synchronized (lock) {
cartRef = (<AtomicReference<ShoppingCart>)session.getAttribute("shoppingCart");
if (cartRef == null) {
cartRef = new AtomicReference<ShoppingCart>();
session.setAttribute("shoppingCart", cartRef);
}
}

After the above code has executed, your Session is initialized. The AtomicReference is guaranteed to be in the session, and in a thread-safe manner. You can either update the shopping cart object in the same synchronized block (and dispense with the AtomicReference all together -- just put the cart itself into the session), or you can update the AtomicReference with code shown earlier above. Which is better depends on how much initialization you need to do, how long it will take to perform this initialization, on whether doing everything in the synchronized block will hurt performance too much (which is best determined with a profiler, not with a guess), and so on.

Normally, in my own code, I just use a synchronized block and don't use Goetz's AtomicReference trick. If I ever determined that synchronization was causing a liveness problem in my applications, then I would potentially move some more expensive initializations out of synchronized blocks by using tricks like the AtomicReference trick.

See also: Is HttpSession thread safe, are set/get Attribute thread safe operations?

In what case it will be not thread-safety

AFAIK, nothing prevents the servlet container to return a different object each time a thread asks for the session (i.e. a proxy over the real, actual session).

If the container does that, synchronizing on the session won't be of any help, since each thread will synchronize on a different object.

The easiest way to have a thread-safe counter is to use an AtomicInteger, and call one of its increment methods, but that doesn't prevent two concurrent threads from storing an AtomicInteger for the first time if they both see it as null.

The easiest way to be sure (although probably not the fastest) is to use a global lock to get the attribute:

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;
}

That said, in a clustered application, the session is made persistent or replicated across nodes of the clustered, so that doesn't bring any guarantee either. Storing the counter in the database would be the solution here.

Is javax.servlet.ServletContext set/getAttribute thread safe?

You can safely assume you can call getAttribute and setAttribute without synchronizing on anything, but you should make the objects you store there threadsafe (the easiest way being to store things that are immutable). The question linked in the comments is about storing a mutable object in the servletContext, in which case threads using it need to acquire its lock first (which the accepted answer explains).

There's not a spelled-out requirement. This is covered in Java Concurrency in Practice, section 4.5.1 Interpreting Vague Documentation:

You are going to have to guess. One way to improve the quality of your guess is to interpret the specification from the perspective of someone who will implement it (such as a container or database vendor), as opposed to someone who will merely use it. Servlets are always called from a container-managed thread, and it is safe to assume that if there is more than one such thread, the container knows this. The servlet container makes available certain objects that provide service to multiple servlets, such as HttpSession or ServletContext. So the servlet container should expect to have these objects accessed concurrently, since it has created multiple threads and called methods like Servlet.service from them that could reasonably be expected to access the ServletContext.

Since it is impossible to imagine a single-threaded context in which these objects would be useful, one has to assume that they have been made thread-safe, even though the specification does not explicitly require this. Besides, if they required client-side locking, on what lock should the client code synchronize? The documentation doesn't say, and it seems absurd to guess. This “reasonable assumption” is further bolstered by the examples in the specification and official tutorials that show how to access ServletContext or HttpSession and do not use any client-side synchronization.

On the other hand, the objects placed in the ServletContext or HttpSession with setAttribute are owned by the web application, not the servlet container. The servlet specification does not suggest any mechanism for coordinating concurrent access to shared attributes. So attributes stored by the container on behalf of the web application should be thread-safe or effectively immutable. If all the container did was store these attributes on behalf of the web application, another option would be to ensure that they are consistently guarded by a lock when accessed from servlet application code. But because the container may want to serialize objects in the HttpSession for replication or passivation purposes, and the servlet container can't possibly know your locking protocol, you should make them thread-safe.



Related Topics



Leave a reply



Submit