EJB Singleton Beans were introduced by the EJB 3.1 specification and are often used to store cached data. This means, we try to improve the performance of our application by using a Singleton. In general, this works quite well. Especially if there are not too many calls in parallel. But it changes if we ignore the default lock and the number of parallel calls increases.
Let’s start with some Java code and see how the sensible default of the lock works out. The following snippet shows a simple EJB Singleton with a counter and two methods. method1 writes the current value of the counter to the log and method2 counts from 0 to 100.
As you can see, there is no lock defined. What do you expect to see in the log file, if we call both methods in parallel?
OK, that might be a little unexpected, the default is a container managed write lock on the entire Singleton. This is a good default to avoid concurrent modifications of the attributes. But it is a bad default if we want to perform read-only operations. In this case, the serializationion of the method calls will result in a lower scalability and in a lower performance under high load.
How to avoid it?
Bean Managed Concurrency
Container Managed Concurrency
We can change this behavior and define read and write locks on method and/or class level. This can be done by annotating the Singleton class or the methods with @javax.ejb.Lock(javax.ejb.LockType). The LockType enum provides the values WRITE and READ to define an exclusive write lock or a read lock.
The following snippet shows how to set the Lock of method1 and method2 to LockType.READ.
OK, if everything works as expect it, both methods should be accessed in paralel. So lets have a look at the log file.
We used the container managed approach to define a read lock for both methods of our singleton. This is not as flexible as the bean managed approach, but it is much easier to use and sufficient in most of the cases. We just need to provide an annotation and the container will handle the rest.