ReentrantReadWriteLock and Lock upgrading.
The Java 5 concurrency library has a lock which supports exclusive and
non-exclusive barriers. Using this lock you can have multiple threads to obtain
read access or one thread to obtain write access.
This can significantly improve performances where there is read contention on an object.
Typical read block
try {
lock.readLock().lock();
// do something
} finally {
lock.readLock().unlock();
}
If you attempt to obtain a write lock while holding the read lock, it will silently block forever.
NOTE: The JVM does not detect this deadlock.
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
try {
// try to obtain lock.
readWriteLock.readLock().lock();
// try to obtain writeLock. This fails as upgrading results in a deadlock.
assertFalse(readWriteLock.writeLock().tryLock(100, TimeUnit.MILLISECONDS));
} finally {
readWriteLock.readLock().unlock();
}
You can code a wrapper to detect deadlock conditions. The RWLock can be used to either detect this deadlock or even allow upgrading.
Detect a deadlock condition
ReadWriteLock readWriteLock = new RWLock("test_upgradeLock2", false);
try {
// try to obtain lock.
readWriteLock.readLock().lock();
// try to obtain writeLock. This fails as upgrading results in a deadlock.
fail("Expected IllegalMonitorStateException, got=" + readWriteLock.writeLock().tryLock(100, TimeUnit.MILLISECONDS));
} catch (IllegalMonitorStateException expected) {
assertEquals("test_upgradeLock2: Cannot be upgraded from readLock to writeLock", expected.getMessage());
} finally {
readWriteLock.readLock().unlock();
}
The RWLock can be configured to allow upgrading.
However for performance reason you are better off changing your code so that it doesn't attempt to upgrade the lock.
Allows upgrades
ReadWriteLock readWriteLock = new RWLock("test_upgradeLock3", true);
boolean okay = false;
try {
// try to obtain lock.
readWriteLock.readLock().lock();
// try to obtain writeLock. This succeeds as .
assertTrue(readWriteLock.writeLock().tryLock(100, TimeUnit.MILLISECONDS));
okay = true;
} finally {
// if the test failed do not obscure the cause with another exception.
try {
readWriteLock.writeLock().unlock();
} catch (RuntimeException ignored) {
if (okay)
throw ignored;
}
try {
readWriteLock.readLock().unlock();
} catch (RuntimeException ignored) {
if (okay)
throw ignored;
}
}
Detecting that a thread holds a lock.
Holding a lock while performing an unrelated long running task can seriously impact your performance.
It is not a deadlock condition, it just slows down your application.
Detecting that a lock is being held
if (RWLock.isDebug())
RWLock.checkUnlocked("read from " + name + ' ' + objectName);
Accessing disk or waiting for a network request can be undesirable
However, using RWLock has its own overhead so its use can be selectable.
// Uses System property "rwlock.debug" to enable.
ReadWriteLock lock = RWLock.createLock(name);
Downloads