About Thorben Janssen

Thorben Janssen is a senior developer with more than 10 years of experience in Java EE development and architecture. During these years he acted as developer, architect, project and/or technical lead to create high available, clustered mobile billing solutions and laboratory information management systems.

Java EE Pitfalls #1: Ignore the default lock of a @Singleton

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.

Sensible defaults

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.

@Singleton
@Remote(SingletonRemote.class)
public class DefaultLock implements SingletonRemote {
    Logger logger = Logger.getLogger(DefaultLock.class.getName());

    private int counter = 0;

    @Override
    public void method1() {
        this.logger.info("method1: " + counter);
    }

    @Override
    public void method2() throws Exception {
        this.logger.info("start method2");
        for (int i = 0; i < 100; i++) {
            counter++;
            logger.info("" + counter);
        }
        this.logger.info("end method2");
    }
}

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?

2014-06-24 21:18:51,948 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 5) method1: 0
2014-06-24 21:18:51,949 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) start method2
2014-06-24 21:18:51,949 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 1
2014-06-24 21:18:51,949 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 2
2014-06-24 21:18:51,950 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 3
  ...
2014-06-24 21:18:51,977 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 99
2014-06-24 21:18:51,977 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) 100
2014-06-24 21:18:51,978 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 4) end method2
2014-06-24 21:18:51,978 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 6) method1: 100
2014-06-24 21:18:51,981 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 7) method1: 100
2014-06-24 21:18:51,985 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 8) method1: 100
2014-06-24 21:18:51,988 INFO  [blog.thoughts.on.java.singleton.lock.DefaultLock] (EJB default - 9) method1: 100

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?

The answer to that question is obvious, we need to take care of the concurrency management. As usual in Java EE, there are two ways to handle it. We can do it ourself or we can ask the container to do it.

Bean Managed Concurrency

I do not want to go into too much detail regarding Bean Managed Concurrency. It is the most flexible way to manage concurrent access. The container allows the concurrent access to all methods of the Singleton and you have to guard its state as necessary. This can be done by using synchronized and volatile. But be careful, quite often this is not as easy as it seems.

Container Managed Concurrency

The Container Managed Concurrency is much easier to use but not as flexible as the bean managed approach. But in my experience, it is good enough for common use cases.

As we saw in the log, container managed concurrency is the default for an EJB Singleton. The container sets a write lock for the entire Singleton and serializes all method calls.

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.

@Singleton
@Remote(SingletonRemote.class)
public class ReadLock implements SingletonRemote {
    Logger logger = Logger.getLogger(ReadLock.class.getName());

    private int counter = 0;

    @Override
    @Lock(LockType.READ)
    public void method1() {
        this.logger.info("method1: " + counter);
    }

    @Override
    @Lock(LockType.READ)
    public void method2() throws Exception {
        this.logger.info("start method2");
        for (int i = 0; i < 100; i++) {
            counter++;
            logger.info("" + counter);
        }
        this.logger.info("end method2");
    }
}

As already mentioned, we could achieve the same by annotating the class with @Lock(LockType.READ) instead of annotating both methods.

OK, if everything works as expect it, both methods should be accessed in paralel. So lets have a look at the log file.

2014-06-24 21:47:13,290 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 10) method1: 0
2014-06-24 21:47:13,291 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) start method2
2014-06-24 21:47:13,291 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 1
2014-06-24 21:47:13,291 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 2
2014-06-24 21:47:13,291 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 3
   ...
2014-06-24 21:47:13,306 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 68
2014-06-24 21:47:13,307 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 69
2014-06-24 21:47:13,308 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 3) method1: 69
2014-06-24 21:47:13,310 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 70
2014-06-24 21:47:13,310 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 71
   ...
2014-06-24 21:47:13,311 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 76
2014-06-24 21:47:13,311 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 77
2014-06-24 21:47:13,312 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 2) method1: 77
2014-06-24 21:47:13,312 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 78
2014-06-24 21:47:13,312 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 79
   ...
2014-06-24 21:47:13,313 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 83
2014-06-24 21:47:13,313 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 84
2014-06-24 21:47:13,314 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 5) method1: 84
2014-06-24 21:47:13,316 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 85
2014-06-24 21:47:13,316 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 86
2014-06-24 21:47:13,317 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 87
2014-06-24 21:47:13,318 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 88
2014-06-24 21:47:13,318 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 6) method1: 89
2014-06-24 21:47:13,318 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 89
2014-06-24 21:47:13,319 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 90
   ...
2014-06-24 21:47:13,321 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 99
2014-06-24 21:47:13,321 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) 100
2014-06-24 21:47:13,321 INFO  [blog.thoughts.on.java.singleton.lock.ReadLock] (EJB default - 1) end method2

Conclusion

At the beginning of this article, we found out that Java EE uses a container managed write lock as default. This results in a serialized processing of all method calls and lowers the scalability and performance of the application. This is something we need to have in mind when implementing an EJB Singleton.

We had a look at the two exisiting options to control the concurrency management: the Bean Managed Concurrency and the Container Managed Concurrency.

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.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

Leave a Reply


7 − = five



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy | Contact
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close