Thursday, 22 April 2010

Thread safety, Java Beans and Hibernate

I just read "Java Concurrency in Practice" by Brian Goetz et al and found it to be a fantastic educational experience. The main thing I learned is that I am a total novice when it comes to multi-threaded programming! My excuse is that I learned programming by building websites and applications, where it is popularly imagined that multi-threading is not really an issue. Also, I'm no computer scientist, just an ex-physicist who found out that pretending to know about the internet and programming pays better than doing a real job like physics. I digress.

I started to think about the standard patterns and practices used by web programmers when building web applications, especially using my favourite frameworks (Spring and Hibernate, of course) and wondered whether I should be more worried about threads than I usually am (i.e. not at all). After some serious head-scratching and blog-reading I have come to the following conclusions.

  1. There is evidence that Java Bean based frameworks could potentially be vulnerable to multi-threading issues due to the mutability of Java Beans
  2. I wouldn't worry about it too much (as long as they are thread-confined).

I better make it clear that I am not talking about the thread-safety of Hibernate or Spring themselves. By and large, as developers we accept these frameworks and given and while we should be aware of the thread-safety issues that they may have there is not much that we can do to change them in our applications. I'm talking about the standard components that we build on top of these frameworks. The thread-safety or otherwise of these frameworks is probably a worthy topic in itself, but it's not what I'm looking at here.

Let's start with Hibernate itself. Typically, persistent classes follow the Java Beans "pattern" of a no-args constructor with accessor and mutator objects for the private fields of the class (getters and setters). Java Beans are intrinsically "mutable" objects (they have mutators, after all!). Mutability on its own is not necessarily a thread-safety hazard, but the first recommendation for building thread-safe classes is to make them immutable - so Java Beans immediately seem to be a thread-safety hazard.

There are plenty of great resources explaining why mutable classes pose thread-safety hazard, including the "Java Concurrency In Practice" book, so I'll just summarise. The Java memory model makes no particular guarantees about the visibilty of changes in state between two threads, and in fact makes no guarantees about the order in which changes in state may seem to occur in two threads. This means that if thread 1 changes the state of a Java Bean field, thread 2 may not see this change immediately, or indeed at all. A further risk is that fields that are involved in invariants (i.e. are constrained by validation rules) could be modified so as to violate the invariants by different threads, even if the mutator methods contain logic to enforce the invariants.

So if Hibernate persistent classes are usually Java Beans, and Java Beans are mutable, and mutability is bad this surely seems to point to a potential thread-safety issue. So why am I sanguine about this risk?

The reason is that in general the risks posed by mutability are reduced when the objects in questions can be "thread-confined". In other words, they are only ever used by a single thread at a time. A mutable class will not pose thread-safety hazards if it is confined in this way. So, are Hibernate Java Beans thread-confined in a web application? The answer is potentially yes, depending on whether you store them somewhere that another thread can access them (like the HttpSession).

If your web application loads a Hibernate entity from a DAO or a service object that uses JPA (for instance) and then places this entity in a request for rendering in a view, it's pretty clear that this is thread-confined up to the point where the entity is placed in the request. Up to this point, it is only ever a local variable in a series of methods and is hence thread-confined (Actually, I don't know whether Hibernate is returning a unique instance for each call to Session.load() but I believe it does).

For example, here is a very brief illustration of retrieving a Hibernate entity for display on a page.

In the service object:
public Customer getCustomer(Integer customerId) {
return em.find(Customer.class, customerId);
}
in the controller:
@ModelAttribute("customer")
public Customer getCustomer() {
return customerService.getCustomer(customerId);
}

In the above example, the Customer entity instance is always thread confined. As a @ModelAttribute is stored in the HttpServletRequest it should be thread-confined as each request runs in its own thread which ends when the request is finished.

However, if the model is stored in the HttpSession, ServletContext or in a cache, then multiple threads might access the JavaBean. You are at the mercy of the thread safety of the HttpSession, ServletContext or cache provider.

In fact, it is recommended to synchronize access to attributes stored in the HttpSession unless contained in an immutable object, because multiple threads may access it simultaneously. The same is true of the SerlvetContext. A well-designed cache should be designed with concurrency in mind and have appropriate synchronization - but you should make sure of this.

So in conclusion, if you want to avoid thread-safety problems with Java Beans, confine them to a single thread and avoid storing them in places where multiple threads could access them such as the HttpSession, ServletContext or a cache (unless you are sure that the cache is thread-safe).

No comments:

Post a Comment