Hi guys, I've recently done some reading about treads and concurrency ( in Java Concurrency in Practice and Patterns of Enterprise Application Architecture ) and this made me ponder on what happens in a Spring Web application and how many zillions of possible problems I overlooked. So I'm trying to figure out how things actually work and maybe you can help me with that.
From what I can tell, we have objects like in this (poorly sketched) image.
I'm not pretty sure what's happening inside Hibernate but I'm guessing as long as i write the transactions ok, Hibernate takes care of the rest.
Then we have usually one instance (bean) for a each dao object and for every layer above, up to controller, which i guess is still one instance created at the initial scan for controllers (i use the @Controller annotation). Further more, all these objects run on the same thread.
But the requests "must" come from separates threads (managed by Tomcat or whatever container). So all the threads use the same controller object -> I already spot problems.
Simultaneous calls to the same method that modifies some data is trouble. I don't know how this propagates to Hibernate but if any of the objects on the way hold any data structure and we don't have synchronized read/write to it, problems are bound to appear.
Am I right or just paranoid? And if I am, do you usually use normal synchronization/locking solutions to solve the problems or is Spring giving a hand?
Concurrency can be a problem in Spring MVC, or any other web based application because , as you said, each request is a different thread.
For example, if you created some kind of user counter object you wanted to use to increment every time someone logged into the application, and set it as a class level attribute in a controller, and called methods on it inside the handleRequest() method of the controller, you'd quickly run into synchronization problems.
Luckily, there are some ways to get around this in a web application.
If the web application uses a database, it will enforce atomic transactions, so you can use a table to back the user counter object and not worry about threading issues. (You may still have to worry about transaction issues though, like insuring you start a transaction, read the current number, add 1, write the new number, then commit the transaction. If you start and stop transactions in between these steps you may end up with wrong results - just like unsynchronized code.)
Another way web applications deal with synchronization issues is the idea of scopes. The J2EE spec specifies page, result, session, and application scopes. You don't need to worry about synchronization at all in page and request scope objects. You usually don't have to worry about it in session objects either. There are some edge cases where a user might have multiple tabs or windows sharing the session where you might need to worry about synchronization. Objects in application scope will need to worry about synchronization, because they're shared by all the users/requests in the application. Unfortunately, a user counter object would have to reside in application scope, so (unless it's backed by a database) you'll need to ensure it's thread safe.
Most web applications don't need to worry about threading issues because most are directly backed by a database, and because most objects used by web apps usually reside in request or session scope. If you get outside of this, you'll need to worry about synchronization. (Things like building your own caching layer on top of the database, storing objects in application scope, or putting objects as class level or static attributes in a class.)
Write once, run anywhere, because there's nowhere to hide! - /. A.C.
Joined: Jul 09, 2008
Thanks Nathan! It's good I gave this a tought now, at the begining.
As a side note: Martin Fowler, the writer of Patterns of Enterprise Application Architecture, metions that db transactions don't fully solve the concurency problems. So probably people will have to keep this in mind if they don't use tools that solve the problems for them (like Hibernate).
Joined: Jul 09, 2008
As another sidenote/sidequestion:
Is it correct that if a bean (for example the controller) would have scope="request" or perhaps "session", we would not have to worry with concurency problems because that bean is isolated (one bean instance for each request/session)? Of course, this is not a practical solution as we can't do this for all the beans, and I can't think why we would have state in a bean that is brand new for every request, but for the sake of theory...
Right, if a bean is in request scope, a new instance is created per request, and each request is it's own thread, so there's no synchronization issues. (Unless you pass this bean into something that does start creating multiple threads, but that's not really a common thing in web apps. ) You can usually treat beans in session scope the same way, with the caveat that it is technically still possible for multiple requests to try to access the bean at the same time - if a user has multiple windows or tabs open, and hits submit on both pages close enough together that through network latency/server processing/cosmic rays/etc. the requests get processes at the same time on the server. Again, this is possible, but unlikely under normal circumstances.
Personally, I wouldn't put state into my Controllers, Services or DAOs. Then I wouldn't ever have to worry about concurrency there. If there was some reason why I might even thought of putting state in there, like say the counter example. I would look for a totally different solution/design for it rather than add concurrency issues. I think there would always be a nice clean easy solution design for those cases.