First8 staat voor vakmanschap. Al onze collega’s zijn een groot aanhanger van Open Source en in het bijzonder het Java-platform. Wij zijn gespecialiseerd in het pragmatisch ontwikkelen van bedrijfskritische Java toepassingen waarbij integratie van systemen, hoge eisen aan beveiliging en veel transacties een belangrijke rol spelen. Op deze pagina vind je onze blogs.

Readers-writer lock using java 5’s condition variables

When I explained a piece of code to a colleague during a code review, I realized I had inadvertently implemented a readers-writer lock. So, I threw the code out, replacing it by the ReadWriteLock from java. This article is about that thrown out code. I updated the method names and comments to reflect the readers-writer lock as opposed to the local business domain.

First, we will look at condition variables. Secondly, we will implement a readers-writer lock using those condition variables. Lastly we will unit test the lock using Awaitility.

Condition variables

Flow-chart of conditions variables
Fig 1. Flow chart of condition variables

Condition variables allow a thread to block while waiting for a condition to change. To do so, a thread acquires the lock before checking the condition, then either conditions are met and the thread proceeds while holding the lock. Or, when the conditions are not met, the thread will block and the lock will be released immediately. Then, when another thread makes a change that might be of interest, it will signal that a change was made, allowing the original thread to wake up, reacquire its lock and recheck the condition. See Fig 1 for a graph of the operation.

A key insight here, is that condition variables are not variables, nor do they ‘do’ anything with a condition. Instead, a condition variable allows to signal that a change has occurred, which will wake up a thread awaiting the same condition variable.

Fig 3. Locked for writing
Fig 2. Locked for reading

Readers-writer lock

A readers-writer lock is a solution for a common problem. When writing to a shared resource, no other thread should write to or read from that resource. But when reading from a resource, we only require that no other thread is writing. Therefore, we can have either one writer, or multiple readers, hence the name readers-writer lock.

Implementation

In the implementation we keep track of the number of active readers. This is not an atomic integer, nor is it marked as volatile, because access to it is guarded by a lock. The field contains either the number of readers, or it marks a write lock, using the value -1. So if activeReaders > 0 then the lock is locked for reading, and when activeReaders = -1 it is locked for writing.

onChange is the condition variable. It is created from the lock.

When acquireReadLock() calls onChange.await(), it will block and suspend that thread. When releaseWriteLock() calls onChange.signal(), the other thread will be woken up, so it can recheck the number of active readers.

There are 2 more similar methods, acquireWriteLock() and releaseReadLock().

Test using Awaitility

The Awaitility library provides methods to test asynchronous method calls. Asynchronous calls will run in another thread, if we check the result too soon, our tests will fail. So we must loop until either our patience runs out, or the assertion is valid. Fortunately, Awaitility does this for us, its API allows us to specify not just what we want to test, but how long we are willing to wait for it.

In the last line, Awaitility is used to wait until the task is done. The task should finish shortly after lock.releaseWriteLock(), but not immediately. Therefore, testing that it finished immediately would fail. So,  Awaitility polls until the assertion succeeds, for at most a second.

Full source code

https://github.com/First8/readers-writer-lock-and-awaitility

Background information

https://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReentrantLock.html

https://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Condition.html

https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock

https://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/ReadWriteLock.html