2

There is a sample usage about lock downgrading in the doc of ReentrantReadWriteLock(see this).

class CachedData {
    final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    Object data;
    volatile boolean cacheValid;

    void processCachedData() {
        rwl.readLock().lock();
        if (!cacheValid) {
            // Must release read lock before acquiring write lock
            rwl.readLock().unlock();
            rwl.writeLock().lock();
            try {
                // Recheck state because another thread might have
                // acquired write lock and changed state before we did.
                if (!cacheValid) {
                    data = ...
                    cacheValid = true;
                }
                // Downgrade by acquiring read lock before releasing write lock
                rwl.readLock().lock();//B
            } finally {//A
                rwl.writeLock().unlock(); // Unlock write, still hold read
            }
        }
        try {
            use(data);
        } finally {//C
            rwl.readLock().unlock();
        }
    }
}

If I change Object data to volatile Object data, should I still need downgrading write lock to read lock?


update

What I mean is if I add volatile to data,Before I release the write lock in finally block at comment A,should I still need acquiring the read lock as the code at commentBandC do? Or the code can take the advantage of volatile?

1 Answers1

3

No, volatile is not needed whether you downgrade or not (the locking already guarantees thread-safe access to data). It also won't help with the atomicity, which is what the acquire-read-then-write-lock pattern does (and which was the point of the question).

You're talking about needing to downgrade like it's a bad thing. You can keep a write lock and not downgrade, and things will work just fine. You're just keeping an unnecessarily strong lock, when a read lock would suffice.

You don't need to downgrade to a read lock, but if you don't it'll make your code less efficient: if use(data) takes 2 seconds (a long time), then without lock downgrading you're blocking all other readers for 2 seconds every time you refresh the cache.

If you mean why do you even need the read lock once the cache refresh is done, it's because otherwise it would be possible for another thread to start a new cache refresh (as there wouldn't be any locks) while we're still working on use(data).

In the given example code it's not possible to determine whether it would actually matter since there's not enough information, but it would create a possible additional state for the method and that's not an advantage:

  • One or more threads are in use(data), having read locks
  • One thread is refreshing cache, having write lock
  • One thread is in use(data) without lock and one thread is refreshing cache with a write lock
Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • Oh,my question is not clear.I mean if I use `volatile` to `data`,should I still need acquiring a read lock before release write lock.I don't konw whether the visibility `volatile` provide can protect `data` or not. – muzi_chelsea Jan 21 '18 at 03:02
  • @muzi_chelsea You need the locks because there are *two* variables here that must be kept in sync and read and written as one atomic operation. If there was only one, you wouldn't. – user207421 Jan 21 '18 at 04:09