Categories
Jakarta EE

Using Locks in Jakarta EE CDI

Jakarta EE applications are multi-threaded. The code to generate the response for the users’ requests runs in his own thread. This seems natural as we like to support multiple users at the same time.

For each user, data is retrieved and processed to return to the user. There are situations where data is coming from a single source and some synchronisation is required. Reading the data is no problem, but when we want to update them, we should have the guarantee that all data is updated in one go. So that threads that are reading the data see consistent data.

The Jakarta EE EJB Specification has the @Lock annotation to handle the synchronisation aspects of the method within an EJB Singleton bean. This blog describes what you can do if your application makes only use of CDI beans or when you want to synchronise the access to a data structure from within different beans.

JVM ReentrantReadWriteLock

The JVM class ReentrantReadWriteLock is created for the use case that is described in the introduction.

An instance maintains a pair of associated locks, one for read-only operations and one for writing. The read lock may be held simultaneously by multiple reader threads, so long as there are no writers. The write lock is exclusive.

Every piece of code that access our shared data for retrieving some information, not updating, must be protected by the read lock that is provided by the instance. The method, can be multiple ones if needed, or statements that update the data structure need to have the write lock before they can proceed.

This way, reading is possible by one or more threads simultaneously unless another part of your code has the write lock at the moment. And changing the data can only be done when no one is reading or they have to wait until the update is completed.

In a central location, a JVM singleton for example, we instantiate the object.

ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

The pattern for reading from the data structure is

Lock lock = readWriteLock.readLock()
try {
   lock.lock();

    // All the read statements go here

} finally {
   lock.unlock();
}

A similar pattern for changing the data structure based on the write lock.

Lock lock = readWriteLock.writeLock()
try {
   lock.lock();

    // All the write statements go here

} finally {
   lock.unlock();
}

There exists also a variant that you do not wait indefinitely to obtain the lock, but an InterruptedException is thrown if the lock can’t be acquired within the time limit that is specified.

More info can be found on the readme page of the repository.

A CDI annotation

The above-described solution can be turned into a CDI interceptor that performs the tasks, similar to the @Lock annotation of Jakarta EE EJB specification.

You can create this yourself, or you can make use of the Atbash Named Lock library that is now available.

For Jakarta EE 8, add the following dependency to your project.

     <dependency>
         <groupId>be.atbash.cdi</groupId>
         <artifactId>locked</artifactId>
         <version>1.0-SNAPSHOT</version>
     </dependency>

The snapshot version is already available on Maven central. The final version will be Ade available in a couple of weeks. Together with the version based on the jakarta namespace so that you can use it with Jakarta EE 9.

You now have the be.atbash.cdi.lock.Locked annotation that you can use on any CDI method

@Locked
public String getValues() {

To use the generic read lock. Or the method that updates the data structure and no other reading should be in progress

@Locked(operation = Locked.Operation.WRITE)
public void writeValue(String value) {

But as the name suggest, you can also specify the name of the lock that you want to use. And thus have more than one lock available within your application.

@Locked(name = "special")

This annotation will operate on another instance of the ReentrantReadWriteLock class and thus work with locks that are indecent of the generic named ones.

Side effect

The write lock, without using the read lock from the pair, can be used to synchronise a method.

@Locked(operation = Locked.Operation.WRITE)
public void writeValue(String value) {

This declaration means that only one thread at a time can execute the method writeValue, which has the same effect as the synchronized keyword from the java language. When the annotation is used on multiple CDI methods, you have the same effect as using a synchronized block that make use of a singleton object so that at any given time, only one annotated method within the entire JVM can be executed at the same time.

Conclusion

The Named locks for CDI is a small library that brings the capabilities of the @Lock from the EJB specification and the ReentrantReadWriteLock JVM class as an annotation to any Jakarta runtime.
It allows you to use the same philosophy of Jakarta EE, concentrate on the user logic, and infrastructure-related aspects are handled automatically or by annotation, for the synchronisation requirements to protect the access to a shared data structure within your application.

This website uses cookies. By continuing to use this site, you accept our use of cookies.  Learn more