How to wait for data with ReentrantReadWriteLock?

一曲冷凌霜 提交于 2019-12-01 18:18:50

The ReentrantReadWriteLock is indeed a bit confusing because the readLock doesn't have a condition. You have to upgrade to a writeLock in your reader only to wait for the condition.

In the writer.

writeLock.lock(); //locks all readers and writers
// do write data
hasData.signalAll();
writeLock.unlock();

In reader you do:

readLock.lock(); //blocks writers only
try{
 if(!checkData()) //check if there's data, don't modify shared variables
 {
  readLock.unlock();
  writeLock.lock(); // need to lock the writeLock to allow to use the condition.
                    // only one reader will get the lock, other readers will wait here      
  try{
   while(!checkData()) // check if there' still no data
   {
     hasData.await(); //will unlock and re-lock after writer has signalled and unlocked.
   }
   readLock.lock();    // continue blocking writer
  }
  finally
  {
    writeLock.unlock(); //let other readers in
  }
 }
 //there should be data now
 readData(); // don't modify variables shared by readers.
}
finally
{
  readlock.unlock(); //let writers in
}

For completeness, each unlock() should be in a finally block, of course.

But how to write a reader? Should it acquire only readLock? But how it can wait for a signal then? If it aquires also a writeLock then where is the supremacy fo read/write locking?

I'd switch to using a BlockingQueue that will take care of all of this for you. Your readers can call queue.take() which blocks waiting for there to be elements in the queue.

Your writer is a bit more complicated. What I'd do is something like the following:

// initially try to put an element into the queue
if (!queue.offer(element)) {
   // if the queue is full then take an element off the head and just drop it
   // this won't block and may not remove anything due to race conditions
   queue.poll();
   // this put will never block because now there will be space in the queue
   queue.put(element);
}

This won't work if there are multiple writers. You'd need a synchronized lock then. If you are dealing with a fixed size queue then the ArrayBlockingQueue should work well.

You cannot achieve a non-blocking behavior using primitives which have a blocking behavior. If you really want the writer to "writes and never waits for anybody", he should not even know the locks you mentionned exists.

When you executes

 rwl.writeLock().lock();

The writer will wait if there are readers operating.

You should try to use wait-free (at least lock-free) primitives if you want to respect the "never wait" condition. For instance, use a ConcurrentLinkedQueue and a locking mechanism which will be only used to manage the race conditions between the readers.

What you need is LinkedBlockingQueue which provides two separate locks takeLock and putLock.

Offer,put method of the queue always use putLock where as take method always use takeLock

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!