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?
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
In Java's implementation locking, there is no way to atomically upgrade a lock from a read lock to write lock. For example, the following code snippet fails
ReentrantReadWriteLock lock = new ...
lock.readLock().lock();
boolean mustWrite = false;
// do somestuff and determine you must instead write! :-O
if(mustWrite) {
lock.writeLock().lock();
writeSomeStuff();
lock.writeLock().unlock();
}
lock.readLock.unlock();
The write lock acquisition has to wait for all read locks are done so it knows it's not overwriting data that readers might potentially be reading. That's bad. So the work around is to do something like this:
if(mustWrite) {
lock.readLock().unlock(); // let go of read before writing
lock.writeLock().lock();
writeSomeStuff();
lock.writeLock().unlock();
lock.readLock().lock(); // get back to reading
}
But this isn't ideal - someone might go and get do something in between when you unlock the read and pick up the write. Now it's probably a good idea to double check those conditions anyway, but still - it's ugly.
Now typically, when you acquire a lock you want to force your code to wait for it to actually acquire before you go doing what you're doing. You wouldn't want to just trust your locking mechanism that it will have given you your lock before you start messing with the data.
But why does it force you to halt execution from when you've signaled that you want the lock to when you're actually read to wait? For example, why couldn't it allow something like this:
lock.writeLock().notifyIntentToLock(); // puts you in line to get the write lock
// everyone else will block until you both
// acquire and release the lock
lock.readLock().unlock(); // proceed with the unlock so you don't deadlock
lock.writeLock().makeGoodOnIntentToLock(); // actually acquire that lock
So in a sense, the current lock functionality could be theorized as them both being done at the same time, like
public void lock() {
this.notifyIntentToLock();
this.makeGoodOnIntentToLock();
}
What design decisions would make them not allow some kind of delayed intent to lock? Is there a serious problem with a lock design like that that I'm simply not seeing?
All you have to do after the decision to take the exclusive lock is:
leave the read lock,
take the write lock and
check the condition again
based on the result, either proceed or bail out.
As for intents to take write locks, what happens when multiple concurrent intents exist? Possibly all of them have to check the initial conditions as the there is no way to ensure who would be victims (granted the lock after the winner).
There is more to that - the impl. of RW lock sucks to boot as the reads modify the metadata causing coherency traffic - hence RW locks don't scale well.
I need a ReadWriteLock that is NOT reentrant, because the lock may be released by a different thread than the one that acquired it. (I realized this when I started to get IllegalMonitorStateException intermittently.)
I'm not sure if non-reentrant is the right term. A ReentrantLock allows the thread that currently holds to lock to acquire it again. I do NOT want this behaviour, therefore I'm calling it "non-reentrant".
The context is that I have a socket server using a thread pool. There is NOT a thread per connection. Requests may get handled by different threads. A client connection may need to lock in one request and unlock in another request. Since the requests may be handled by different threads, I need to be able to lock and unlock in different threads.
Assume for the sake of this question that I need to stay with this configuration and that I do really need to lock and unlock in different requests and therefore possibly different threads.
It's a ReadWriteLock because I need to allow multiple "readers" OR an exclusive "writer".
It looks like this could be written using AbstractQueuedSynchronizer but I'm afraid if I write it myself I'll make some subtle mistake. I can find various examples of using AbstractQueuedSynchronizer but not a ReadWriteLock.
I could take the OpenJDK ReentrantReadWriteLock source and try to remove the reentrant part but again I'm afraid I wouldn't get it quite right.
I've looked in Guava and Apache Commons but didn't find anything suitable. Apache Commons has RWLockManager which might do what I need but I'm not sure and it seems more complex than I need.
A Semaphore allows different threads to perform the acquire and release of permits. An exclusive write is equivalent to having all of the permits, as the thread waits until all have been released and no additional permits can be acquired by other threads.
final int PERMITS = Integer.MAX_VALUE;
Semaphore semaphore = new Semaphore(PERMITS);
// read
semaphore.acquire(1);
try { ... }
finally {
semaphore.release(1);
}
// write
semaphore.acquire(PERMITS);
try { ... }
finally {
semaphore.release(PERMITS);
}
I know you've already accepted another answer. But I still think that you are going to create quite a nightmare for yourself. Eventually, a client is going to fail to come back and release those permits and you'll begin to wonder why the "writer" never writes.
If I were doing it, I would do it like this:
Client issues a request to start a transaction
The initial request creates a task (Runnable/Callable) and places it in an Executor for execution
The initial request also registers that task in a Map by transaction id
Client issues the second request to close the transaction
The close request finds the task by transaction id in a map
The close request calls a method on the task to indicate that it should close (probably a signal on a Condition or if data needs to be passed, placing an object in a BlockingQueue)
Now, the transaction task would have code like this:
public void run() {
readWriteLock.readLock().lock();
try {
//do stuff for initializing this transaction
if (condition.await(someDurationAsLong, someTimeUnit)( {
//do the rest of the transaction stuff
} else {
//do some other stuff to back out the transaction
}
} finally {
readWriteLock.readLock.unlock();
}
}
Not entirely sure what you need, esp. why it should be a read write lock, but if you have task that need to be handled by many threads, and you don't want it to be processesd/accessed concurrently, I'd use actually a ConcurrentMap ( etc.).
You can remove the task from the map or substitute it with a special "lock object" to indicate it's locked. You could return the task with an updated state to the map to let another thread take over, or alternatively you can pass the task directly to the next thread and let it return the task to the map instead.
They seem to have dropped the ball on this one by deprecating com.sun.corba.se.impl.orbutil.concurrent.Mutex;
I mean who in his right mind thinks that we won't need non-reentrant locks. Here we are, wasting our times arguing over the definition of reentrant (can slighty change in meaning per framework btw). Yes I want to tryLock on the same thread is that such a bad thing? it won't deadlock because ill else out of it. A non-reentrant lock that locks in the same thread can be very usefull to prevent errors on GUI apps where the user presses on the same button rapidly and repeatedly. Been there, done that, QT was right...again.
I have a producer app that generates an index (stores it in some in-memory tree data structure). And a consumer app will use the index to search for partial matches.
I don't want the consumer UI to have to block (e.g. via some progress bar) while the producer is indexing the data. Basically if the user wishes to use the partial index, it will just do so. In this case, the producer will potentially have to stop indexing for a while until the user goes away to another screen.
Roughly, I know I will need the wait/notify protocol to achieve this. My question: is it possible to interrupt the producer thread using wait/notify while it is doing its business ? What java.util.concurrent primitives do I need to achieve this ?
The way you've described this, there's no reason that you need wait/notify. Simply synchronize access to your data structure, to ensure that it is in a consistent state when accessed.
Edit: by "synchronize access", I do not mean synchronize the entire data structure (which would end up blocking either producer or consumer). Instead, synchronize only those bits that are being updated, and only at the time that you update them. You'll find that most of the producer's work can take place in an unsynchronized manner: for example, if you're building a tree, you can identify the node where the insert needs to happen, synchronize on that node, do the insert, then continue on.
In your producer thread, you are likely to have some kind of main loop. This is probably the best place to interrupt your producer. Instead of using wait() and notify() I suggest you use the java synchronization objects introduced in java 5.
You could potentially do something like that
class Indexer {
Lock lock = new ReentrantLock();
public void index(){
while(somecondition){
this.lock.lock();
try{
// perform one indexing step
}finally{
lock.unlock();
}
}
}
public Item lookup(){
this.lock.lock();
try{
// perform your lookup
}finally{
lock.unlock();
}
}
}
You need to make sure that each time the indexer releases the lock, your index is in a consistent, legal state. In this scenario, when the indexer releases the lock, it leaves a chance for a new or waiting lookup() operation to take the lock, complete and release the lock, at which point your indexer can proceed to its next step. If no lookup() is currently waiting, then your indexer just reaquires the lock itself and goes on with its next operation.
If you think you might have more that one thread trying to do the lookup at the same time, you might want to have a look at the ReadWriteLock interface and ReentrantReadWriteLock implementation.
Of course this solution is the simple way to do it. It will block either one of the threads that doesn't have the lock. You may want to check if you can just synchronize on your data structure directly, but that might prove tricky since building indexes tends to use some sort of balanced tree or B-Tree or whatnot where node insertion is far from being trivial.
I suggest you first try that simple approach, then see if the way it behaves suits you. If it doesn't, you may either try breaking up the the indexing steps into smaller steps, or try synchronizing on only parts of your data structure.
Don't worry too much about the performance of locking, in java uncontended locking (when only one thread is trying to take the lock) is cheap. As long as most of your locking is uncontented, locking performance is nothing to be concerned about.
The producer application can have two indices: published and in-work. The producer will work only with in-work, the consumer will work only with published. Once the producer done with indexing it can replace in-work one with published (usually swapping one pointer). The producer may also publish copy of the partial index if will bring value. This way you will avoid long term locks -- it will be useful when index accessed by lost of consumers.
No, that's not possible.
The only way of notifying a thread without any explicit code in the thread itself is to use Thread.interrupt(), which will cause an exception in the thread. interrrupt() is usually not very reliable though, because throwing a exception at some random point in the code is a nightmare to get right in all code paths. Beside that, a single try{}catch(Throwable){} somewhere in the thread (including any libraries that you use) could be enough to swallow the signal.
In most cases, the only correct solution is use a shared flag or a queue that the consumer can use to pass messages to the producer. If you worry about the producer being unresponsive or freezing, run it in a separate thread and require it to send heartbeat messages every n seconds. If it does not send a heartbeat, kill it. (Note that determining whether a producer is actually freezing, and not just waiting for an external event, is often very hard to get right as well).
I am using in my code at the moment a ReentrantReadWriteLock to synchronize access over a tree-like structure. This structure is large, and read by many threads at once with occasional modifications to small parts of it - so it seems to fit the read-write idiom well. I understand that with this particular class, one cannot elevate a read lock to a write lock, so as per the Javadocs one must release the read lock before obtaining the write lock. I've used this pattern successfully in non-reentrant contexts before.
What I'm finding however is that I cannot reliably acquire the write lock without blocking forever. Since the read lock is reentrant and I am actually using it as such, the simple code
lock.getReadLock().unlock();
lock.getWriteLock().lock()
can block if I have acquired the readlock reentrantly. Each call to unlock just reduces the hold count, and the lock is only actually released when the hold count hits zero.
EDIT to clarify this, as I don't think I explained it too well initially - I am aware that there is no built-in lock escalation in this class, and that I have to simply release the read lock and obtain the write lock. My problem is/was that regardless of what other threads are doing, calling getReadLock().unlock() may not actually release this thread's hold on the lock if it acquired it reentrantly, in which case the call to getWriteLock().lock() will block forever as this thread still has a hold on the read lock and thus blocks itself.
For example, this code snippet will never reach the println statement, even when run singlethreaded with no other threads accessing the lock:
final ReadWriteLock lock = new ReentrantReadWriteLock();
lock.getReadLock().lock();
// In real code we would go call other methods that end up calling back and
// thus locking again
lock.getReadLock().lock();
// Now we do some stuff and realise we need to write so try to escalate the
// lock as per the Javadocs and the above description
lock.getReadLock().unlock(); // Does not actually release the lock
lock.getWriteLock().lock(); // Blocks as some thread (this one!) holds read lock
System.out.println("Will never get here");
So I ask, is there a nice idiom to handle this situation? Specifically, when a thread that holds a read lock (possibly reentrantly) discovers that it needs to do some writing, and thus wants to "suspend" its own read lock in order to pick up the write lock (blocking as required on other threads to release their holds on the read lock), and then "pick up" its hold on the read lock in the same state afterwards?
Since this ReadWriteLock implementation was specifically designed to be reentrant, surely there is some sensible way to elevate a read lock to a write lock when the locks may be acquired reentrantly? This is the critical part that means the naive approach does not work.
This is an old question, but here's both a solution to the problem, and some background information.
As others have pointed out, a classic readers-writer lock (like the JDK ReentrantReadWriteLock) inherently does not support upgrading a read lock to a write lock, because doing so is susceptible to deadlock.
If you need to safely acquire a write lock without first releasing a read lock, there is a however a better alternative: take a look at a read-write-update lock instead.
I've written a ReentrantReadWrite_Update_Lock, and released it as open source under an Apache 2.0 license here. I also posted details of the approach to the JSR166 concurrency-interest mailing list, and the approach survived some back and forth scrutiny by members on that list.
The approach is pretty simple, and as I mentioned on concurrency-interest, the idea is not entirely new as it was discussed on the Linux kernel mailing list at least as far back as the year 2000. Also the .Net platform's ReaderWriterLockSlim supports lock upgrade also. So effectively this concept had simply not been implemented on Java (AFAICT) until now.
The idea is to provide an update lock in addition to the read lock and the write lock. An update lock is an intermediate type of lock between a read lock and a write lock. Like the write lock, only one thread can acquire an update lock at a time. But like a read lock, it allows read access to the thread which holds it, and concurrently to other threads which hold regular read locks. The key feature is that the update lock can be upgraded from its read-only status, to a write lock, and this is not susceptible to deadlock because only one thread can hold an update lock and be in a position to upgrade at a time.
This supports lock upgrade, and furthermore it is more efficient than a conventional readers-writer lock in applications with read-before-write access patterns, because it blocks reading threads for shorter periods of time.
Example usage is provided on the site. The library has 100% test coverage and is in Maven central.
I have made a little progress on this. By declaring the lock variable explicitly as a ReentrantReadWriteLock instead of simply a ReadWriteLock (less than ideal, but probably a necessary evil in this case) I can call the getReadHoldCount() method. This lets me obtain the number of holds for the current thread, and thus I can release the readlock this many times (and reacquire it the same number afterwards). So this works, as shown by a quick-and-dirty test:
final int holdCount = lock.getReadHoldCount();
for (int i = 0; i < holdCount; i++) {
lock.readLock().unlock();
}
lock.writeLock().lock();
try {
// Perform modifications
} finally {
// Downgrade by reacquiring read lock before releasing write lock
for (int i = 0; i < holdCount; i++) {
lock.readLock().lock();
}
lock.writeLock().unlock();
}
Still, is this going to be the best I can do? It doesn't feel very elegant, and I'm still hoping that there's a way to handle this in a less "manual" fashion.
What you want to do ought to be possible. The problem is that Java does not provide an implementation that can upgrade read locks to write locks. Specifically, the javadoc ReentrantReadWriteLock says it does not allow an upgrade from read lock to write lock.
In any case, Jakob Jenkov describes how to implement it. See http://tutorials.jenkov.com/java-concurrency/read-write-locks.html#upgrade for details.
Why Upgrading Read to Write Locks Is Needed
An upgrade from read to write lock is valid (despite the assertions to the contrary in other answers). A deadlock can occur, and so part of the implementation is code to recognize deadlocks and break them by throwing an exception in a thread to break the deadlock. That means that as part of your transaction, you must handle the DeadlockException, e.g., by doing the work over again. A typical pattern is:
boolean repeat;
do {
repeat = false;
try {
readSomeStuff();
writeSomeStuff();
maybeReadSomeMoreStuff();
} catch (DeadlockException) {
repeat = true;
}
} while (repeat);
Without this ability, the only way to implement a serializable transaction that reads a bunch of data consistently and then writes something based on what was read is to anticipate that writing will be necessary before you begin, and therefore obtain WRITE locks on all data that are read before writing what needs to be written. This is a KLUDGE that Oracle uses (SELECT FOR UPDATE ...). Furthermore, it actually reduces concurrency because nobody else can read or write any of the data while the transaction is running!
In particular, releasing the read lock before obtaining the write lock will produce inconsistent results. Consider:
int x = someMethod();
y.writeLock().lock();
y.setValue(x);
y.writeLock().unlock();
You have to know whether someMethod(), or any method it calls, creates a reentrant read lock on y! Suppose you know it does. Then if you release the read lock first:
int x = someMethod();
y.readLock().unlock();
// problem here!
y.writeLock().lock();
y.setValue(x);
y.writeLock().unlock();
another thread may change y after you release its read lock, and before you obtain the write lock on it. So y's value will not be equal to x.
Test Code: Upgrading a read lock to a write lock blocks:
import java.util.*;
import java.util.concurrent.locks.*;
public class UpgradeTest {
public static void main(String[] args)
{
System.out.println("read to write test");
ReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock(); // get our own read lock
lock.writeLock().lock(); // upgrade to write lock
System.out.println("passed");
}
}
Output using Java 1.6:
read to write test
<blocks indefinitely>
What you are trying to do is simply not possible this way.
You cannot have a read/write lock that you can upgrade from read to write without problems. Example:
void test() {
lock.readLock().lock();
...
if ( ... ) {
lock.writeLock.lock();
...
lock.writeLock.unlock();
}
lock.readLock().unlock();
}
Now suppose, two threads would enter that function. (And you are assuming concurrency, right? Otherwise you would not care about locks in the first place....)
Assume both threads would start at the same time and run equally fast. That would mean, both would acquire a read lock, which is perfectly legal. However, then both would eventually try to acquire the write lock, which NONE of them will ever get: The respective other threads hold a read lock!
Locks that allow upgrading of read locks to write locks are prone to deadlocks by definition. Sorry, but you need to modify your approach.
Java 8 now has a java.util.concurrent.locks.StampedLock
with a tryConvertToWriteLock(long) API
More info at http://www.javaspecialists.eu/archive/Issue215.html
What you're looking for is a lock upgrade, and is not possible (at least not atomically) using the standard java.concurrent ReentrantReadWriteLock. Your best shot is unlock/lock, and then check that noone made modifications inbetween.
What you're attempting to do, forcing all read locks out of the way is not a very good idea. Read locks are there for a reason, that you shouldn't write. :)
EDIT:
As Ran Biron pointed out, if your problem is starvation (read locks are being set and released all the time, never dropping to zero) you could try using fair queueing. But your question didn't sound like this was your problem?
EDIT 2:
I now see your problem, you've actually acquired multiple read-locks on the stack, and you'd like to convert them to a write-lock (upgrade). This is in fact impossible with the JDK-implementation, as it doesn't keep track of the owners of the read-lock. There could be others holding read-locks that you wouldn't see, and it has no idea how many of the read-locks belong to your thread, not to mention your current call-stack (i.e. your loop is killing all read locks, not just your own, so your write lock won't wait for any concurrent readers to finish, and you'll end up with a mess on your hands)
I've actually had a similar problem, and I ended up writing my own lock keeping track of who's got what read-locks and upgrading these to write-locks. Although this was also a Copy-on-Write kind of read/write lock (allowing one writer along the readers), so it was a little different still.
What about this something like this?
class CachedData
{
Object data;
volatile boolean cacheValid;
private class MyRWLock
{
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
public synchronized void getReadLock() { rwl.readLock().lock(); }
public synchronized void upgradeToWriteLock() { rwl.readLock().unlock(); rwl.writeLock().lock(); }
public synchronized void downgradeToReadLock() { rwl.writeLock().unlock(); rwl.readLock().lock(); }
public synchronized void dropReadLock() { rwl.readLock().unlock(); }
}
private MyRWLock myRWLock = new MyRWLock();
void processCachedData()
{
myRWLock.getReadLock();
try
{
if (!cacheValid)
{
myRWLock.upgradeToWriteLock();
try
{
// Recheck state because another thread might have acquired write lock and changed state before we did.
if (!cacheValid)
{
data = ...
cacheValid = true;
}
}
finally
{
myRWLock.downgradeToReadLock();
}
}
use(data);
}
finally
{
myRWLock.dropReadLock();
}
}
}
to OP:
just unlock as many times as you have entered the lock, simple as that:
boolean needWrite = false;
readLock.lock()
try{
needWrite = checkState();
}finally{
readLock().unlock()
}
//the state is free to change right here, but not likely
//see who has handled it under the write lock, if need be
if (needWrite){
writeLock().lock();
try{
if (checkState()){//check again under the exclusive write lock
//modify state
}
}finally{
writeLock.unlock()
}
}
in the write lock as any self-respect concurrent program check the state needed.
HoldCount shouldn't be used beyond debug/monitor/fast-fail detect.
I suppose the ReentrantLock is motivated by a recursive traversal of the tree:
public void doSomething(Node node) {
// Acquire reentrant lock
... // Do something, possibly acquire write lock
for (Node child : node.childs) {
doSomething(child);
}
// Release reentrant lock
}
Can't you refactor your code to move the lock handling outside of the recursion ?
public void doSomething(Node node) {
// Acquire NON-reentrant read lock
recurseDoSomething(node);
// Release NON-reentrant read lock
}
private void recurseDoSomething(Node node) {
... // Do something, possibly acquire write lock
for (Node child : node.childs) {
recurseDoSomething(child);
}
}
So, Are we expecting java to increment read semaphore count only if this thread has not yet contributed to the readHoldCount? Which means unlike just maintaining a ThreadLocal readholdCount of type int, It should maintain ThreadLocal Set of type Integer (maintaining the hasCode of current thread). If this is fine, I would suggest (at-least for now) not to call multiple read calls within the same class, but instead use a flag to check, whether read lock is already obtained by current object or not.
private volatile boolean alreadyLockedForReading = false;
public void lockForReading(Lock readLock){
if(!alreadyLockedForReading){
lock.getReadLock().lock();
}
}
Found in the documentation for ReentrantReadWriteLock. It clearly says, that reader threads will never succeed when trying to acquire a write lock. What you try to achieve is simply not supported. You must release the read lock before acquisition of the write lock. A downgrade is still possible.
Reentrancy
This lock allows both readers and writers to reacquire read or write
locks in the style of a {#link ReentrantLock}. Non-reentrant readers
are not allowed until all write locks held by the writing thread have
been released.
Additionally, a writer can acquire the read lock, but not vice-versa.
Among other applications, reentrancy can be useful when write locks
are held during calls or callbacks to methods that perform reads under
read locks. If a reader tries to acquire the write lock it will never
succeed.
Sample usage from the above source:
class CachedData {
Object data;
volatile boolean cacheValid;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
// 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();
rwl.writeLock().unlock(); // Unlock write, still hold read
}
use(data);
rwl.readLock().unlock();
}
}
Use the "fair" flag on the ReentrantReadWriteLock. "fair" means that lock requests are served on first come, first served. You could experience performance depredation since when you'll issue a "write" request, all of the subsequent "read" requests will be locked, even if they could have been served while the pre-existing read locks are still locked.