Referring to Brian Goetz’s article Are all stateful Web applications broken? for IBM developerWorks, I want to refer to this piece of code
HttpSession session = request.getSession(true);
ShoppingCart cart = (ShoppingCart)session.getAttribute("shoppingCart");
if (cart == null) {
cart = new ShoppingCart(...);
session.setAttribute("shoppingCart", cart);
}
doSomethingWith(cart);
From my available understanding, this code is not thread-safe because it uses the check-then-act pattern. But I have a doubt:
Isn’t the creation or retrieval of the HttpSession in the first line totally atomic? By atomic, I mean that if two Threads call request.getSession(), one will block. Although both will return the same HttpSession instance. Thus, if a client (mobile/web browsers) makes two or make calls to the same Servlet (that executes the snippet above), you will never get a situation in which different threads see different values for cart.
Assuming I am convinced that it is NOT thread safe, how would one make this thread safe? Would an AtomicReference work? e.g.:
HttpSession session = request.getSession(true);
AtomicReference<ShoppingCart> cartRef =
(<AtomicReference<ShoppingCart>)session.getAttribute("shoppingCart");
ShoppingCart cart = cartRef.get();
if (cart == null) {
cart = new ShoppingCart(...);
session.setAttribute("shoppingCart",
new AtomicReference<ShoppingCart>(cart));
}
doSomethingWith(cart);
Merci!
Your code is still not Thread-safe:
This is because two Threads can both get a
cartof 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 differentcartobject.To make this Thread-safe, you would need to do something like this, following the idiom from the article you referenced:
With the above code, if two Threads using the same
HttpSessionenter thewhileloop at the same time, there is no data race that can cause them to use differentcartobjects.To address the part of the problem that Brian Goetz doesn’t address in the article, namely how do you get the
AtomicReferenceinto 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 emptyAtomicReferenceobjects into the session in itssessionCreatedmethod: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 yourservice()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:
After the above code has executed, your Session is initialized. The
AtomicReferenceis 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 theAtomicReferenceall together — just put the cart itself into the session), or you can update theAtomicReferencewith 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
AtomicReferencetrick. 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 theAtomicReferencetrick.See also: Is HttpSession thread safe, are set/get Attribute thread safe operations?