【问题标题】:How to wait for data with ReentrantReadWriteLock?如何使用 ReentrantReadWriteLock 等待数据?
【发布时间】:2012-10-26 13:59:35
【问题描述】:

据说ReentrantReadWriteLock 是为一个作者和多个读者准备的。

不过,读者应该等到缓冲区中出现一些数据。

那么,要锁定什么?

我创建了如下并发对象:

private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
protected final Lock readLock = rwl.readLock();
protected final Lock writeLock = rwl.writeLock();
protected final Condition hasData = writeLock.newCondition();

现在我在写方法:

writeLock.lock();

// writing first portion and updating variables

hasData.signalAll();

// if required then writing second portion and updating variables

hasData.signalAll();

但是如何写一个读者呢?它应该只获取readLock吗?但它怎么能等待信号呢?如果它还需要writeLock,那么读/写锁定的至高无上的地位在哪里?

如果所需变量仅受writeLock保护,如何确保它们在读取过程中不会改变?

队列与任务不匹配

这是关于ReentrantReadWriteLock的问题。

【问题讨论】:

  • 您可能会发现 Disruptor 库很有趣,因为它很好地解决了这类问题。 code.google.com/p/disruptor
  • 阻塞队列匹配任务,因为它被解释了。为什么你的意思是在所有情况下都写数据?您可以设置,以便写入永远不会失败。
  • 我已经调整了我的答案@UmNyobe。最初它没有处理她想丢弃最旧的数据以在队列中腾出空间的情况。阻塞队列现在应该可以处理更改了。
  • @UmNyobe 任务没有完整解释。问题是关于ReentrantReadWriteLock,而不是关于如何解决任务。
  • 奇怪的是不是在寻找解决方案,而是无论如何。要考虑@SuzanCioc 的一件事是,如果您正在执行 IO,您很可能会浪费时间过早地优化锁定,因为您的应用程序将受 IO 限制。

标签: java concurrency java.util.concurrent reentrantreadwritelock


【解决方案1】:

ReentrantReadWriteLock 确实有点混乱,因为 readLock 没有条件。 您必须在阅读器中升级到 writeLock 才能等待条件。

在作家中。

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

在阅读器中你这样做:

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
}

为了完整起见,当然,每个 unlock() 都应该在 finally 块中。

【讨论】:

  • 但是如果我也获得了writeLock,那么我如何从单独的锁中受益呢?那不是最好有一把锁吗?
  • 只有在没有数据的情况下获取writeLock,在等待条件的时候隐式释放。只要有数据,多个阅读器就会同时阅读。
【解决方案2】:

但是如何写一个读者呢?它应该只获取 readLock 吗?但它怎么能等待信号呢?如果它还需要一个 writeLock,那么读/写锁定的优势在哪里?

我会改用BlockingQueue,它将为您处理所有这些问题。您的读者可以调用queue.take() 等待队列中有元素的阻塞。

你的作家有点复杂。我要做的是如下所示:

// 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);
}

如果有多个作家,这将不起作用。那么你需要一个synchronized 锁。如果您正在处理固定大小的队列,那么 ArrayBlockingQueue 应该可以正常工作。

【讨论】:

  • 确实 BlockingQueue 是解决这个问题的理想选择。忘记了。
  • 首先,我正在高速发送不同大小的二进制数据。其次,queue.offer() 不会等待,也不会写入任何数据。我需要在所有情况下写入数据。
  • 所以不同大小的二进制数据都可以。例如,它可以是byte[] 的队列。对。我已经编辑了我的答案,以考虑到您总是想添加到队列@SuzanCioc。
  • 队列不匹配。我正在使用InputStreamOutputStream 之类的接口,因此很难实现跨界读取(但我在以前的版本中做到了)。无论如何,任务的描述非常简短,只是为了澄清主要问题。任务信息不足以改进整个解决方案。
  • 我仍然不明白为什么队列不起作用。输入流为您提供byte[]。我看不出ReentrantReadWriteLock 如何让您摆脱@SuzanCioc 的跨界读取问题。
【解决方案3】:

您无法使用具有阻塞行为的原语来实现非阻塞行为。如果你真的想让作者“写而不等任何人”,他甚至不应该知道你提到的锁存在。

当你执行时

 rwl.writeLock().lock();

如果有读者在操作,作者会等待。

如果您想尊重“从不等待”条件,您应该尝试使用无等待(至少无锁)原语。例如,使用ConcurrentLinkedQueue 和锁定机制,该机制仅用于管理读取器之间的竞争条件。

【讨论】:

  • “不等待”我的意思是不要等待条件。但当然我需要一些等待一致性。
【解决方案4】:

你需要的是LinkedBlockingQueue 它提供了两个独立的锁 takeLockputLock

Offer,put 队列的方法总是使用putLocktake 方法总是使用takeLock

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-29
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    相关资源
    最近更新 更多