Skip to content
Advertisement

Implementing a resource read/write lock in Java

I’m trying to implement a simple read/write lock for a resource accessed concurrently by multiple threads. The workers randomly try reading or writing to a shared object. When a read lock is set, workers should not be able to write until the lock is released. When a write lock is set, read and write are not permitted. Although my implementation seems to work, I believe it is conceptually wrong.

A read operation taking place should allow for more read operations happening at the same time, resulting in the overall number of reads being larger than the number of writes. My program yields numbers that follow the probability of these operations being performed by a worker.

I feel like my implementation is actually not concurrent at all, but I’m having a hard time identifying the mistake. I would really appreciate being pointed in the right direction.

Main class that dispatches and terminates workers:

JavaScript

The Resource class:

JavaScript

And finally the Worker class:

JavaScript

The reasoning behind having two separate locks for read and write is that I want to have the ability to atomise both operations and their queries for the lock.

Here is an example of the output I’m getting with a 0.5 write probability:

JavaScript

Help much appreciated.

Advertisement

Answer

You are performing the entire operation within a synchronized block, so there is no concurrency. Further, there is no precedence towards any lock kind, as at most one thread can own a lock. Not performing the entire operation in a synchronized block won’t work with your current code, as every reader does a readLock = ResourceLock.OFF at the end, regardless of how many readers are there. Without a counter, you can’t support multiple readers correctly.

Besides that, it’s a strange code structure, to provide a Resource class maintaining the state but leaving it entirely up to the callers to do the right thing with it. That’s not the way to deal with responsibility and encapsulation.

An implementation may look like

JavaScript

It simply uses a counter of acquired read locks, setting the counter to -1 when there is a write lock (so write locks can not be nested). Acquiring a read lock may succeed whenever there is no write lock, so there is no need to implement precedence for them, the possibility to succeed when another thread already has a real lock, is sufficient. In fact, when having a significantly larger number of readers than writers, you may encounter the “starving writer” problem.

The worker simplifies to

JavaScript

Note that I avoided global variables here. The lock should get passed to the constructor. It’s also important that the methods return when being interrupted during the lock acquisition. Self interrupting and retrying the acquisition like in your original code will lead to an infinite loop, as the next wait would again throw an InterruptedException after you restored the current thread’s interrupted state. Of course, proceeding without having the lock would be wrong too, so the only valid options are not restoring the interrupted state or returning immediately.

The only change to your main program is to construct a pass the lock instance:

JavaScript
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement