在ReentrantReadWriteLock中是否有任何WriteLock获得优先于ReadLock的优先级



我读过java文档:ReentrantReadWriteLock

而且我没有看到writeLock比readLock有任何优先级

但我也读过这样的主题:ReentrantReadWriteLock 中的读写锁是否以某种方式相关?
我在两个答案中看到以下短语:

如果锁定被读取器持有并且线程请求不再写锁定 允许读卡器获取读锁定,直到线程具有 获得的写锁定释放它。


更喜欢作家而不是读者。也就是说,如果作家正在等待 锁定,不允许来自其他线程的新读取器访问 资源。现有读者可以继续使用该资源,直到他们 松开锁。这可以防止所谓的"作家饥饿"。

这些短语听起来很有意义,看起来我已经在其他地方读过了。

但显然这与java文档是矛盾的。

它在最新的JDK中是否更改了?它仍然有效吗?

如何防止作家饥饿?

ReentrantReadWriteLock描述了两种操作模式:公平模式和非公平模式。您引用的选择似乎旨在描述公平模式。我不会说两者都是明显矛盾的。但是,我可以看到第一个措辞不准确("不允许更多读者"(可能会导致混淆。 我怀疑"更多读者"是指来自其他线程的新读者,而不是来自同一线程的额外可重入读取,有些人可能会将其解释为同一读者。 如果以这种方式解释,那么它们似乎与JavaDoc一致。

我已经创建了示例来检查:

public class RWLockTest {
public static final Logger LOGGER = LoggerFactory.getLogger(RWLockTest.class);
public static void main(String[] args) {
SomeClass someClass = new SomeClass();
Reader readerRunnable = new Reader(someClass);
Writer writerRunnable = new Writer(someClass);
//group 1 readers
for (int i = 0; i < 10; i++) {
new Thread(readerRunnable).start();
}
// 2 writers
new Thread(writerRunnable).start();
LOGGER.info("!!!!!!!!!!!!!!!WRITER_1 WAS STARTED!!!!!!!!!!!!!!!");
new Thread(writerRunnable).start();
LOGGER.info("!!!!!!!!!!!!!!!WRITER_2 WAS STARTED!!!!!!!!!!!!!!!");
//group 2 readers            
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(readerRunnable);
LOGGER.info(String.format("%s was submitted", thread.getId()));
thread.start();
}
}
public static class SomeClass {
public ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void read() {
readWriteLock.readLock().lock();
try {
LOGGER.info(String.format("Read by %s started", Thread.currentThread().getId()));
Thread.sleep(5000);
LOGGER.info(String.format("Read by %s finished", Thread.currentThread().getId()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
public void write() {
readWriteLock.writeLock().lock();
try {
LOGGER.info(String.format("!!!!!!!!!!Write by %s started!!!!!!!!!!!!", Thread.currentThread().getId()));
Thread.sleep(3000);
LOGGER.info(String.format("!!!!!!!!!!Write by %s finished!!!!!!!!", Thread.currentThread().getId()));
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
}
public static class Reader implements Runnable {
SomeClass someClass;
public Reader(SomeClass someClass) {
this.someClass = someClass;
}
@Override
public void run() {
someClass.read();
}
}
public static class Writer implements Runnable {
SomeClass someClass;
public Writer(SomeClass someClass) {
this.someClass = someClass;
}
@Override
public void run() {
someClass.write();
}
}
}

我尝试运行大量时间,输出通常是这样的:

16:31:49.037 [main] INFO my.pack.RWLockTest - !!!!!!!!!!!!!!!WRITER_1 WAS STARTED!!!!!!!!!!!!!!!
16:31:49.040 [main] INFO my.pack.RWLockTest - !!!!!!!!!!!!!!!WRITER_2 WAS STARTED!!!!!!!!!!!!!!!
16:31:49.046 [Thread-1] INFO my.pack.RWLockTest - Read by 13 started
16:31:49.046 [main] INFO my.pack.RWLockTest - 24 was submitted
16:31:49.046 [Thread-7] INFO my.pack.RWLockTest - Read by 19 started
16:31:49.046 [Thread-4] INFO my.pack.RWLockTest - Read by 16 started
16:31:49.046 [Thread-5] INFO my.pack.RWLockTest - Read by 17 started
16:31:49.046 [Thread-0] INFO my.pack.RWLockTest - Read by 12 started
16:31:49.046 [Thread-3] INFO my.pack.RWLockTest - Read by 15 started
16:31:49.047 [main] INFO my.pack.RWLockTest - 25 was submitted
16:31:49.046 [Thread-9] INFO my.pack.RWLockTest - Read by 21 started
16:31:49.047 [Thread-8] INFO my.pack.RWLockTest - Read by 20 started
16:31:49.047 [main] INFO my.pack.RWLockTest - 26 was submitted
16:31:49.047 [Thread-2] INFO my.pack.RWLockTest - Read by 14 started
16:31:49.047 [Thread-6] INFO my.pack.RWLockTest - Read by 18 started
16:31:49.047 [main] INFO my.pack.RWLockTest - 27 was submitted
16:31:49.048 [main] INFO my.pack.RWLockTest - 28 was submitted
16:31:49.048 [main] INFO my.pack.RWLockTest - 29 was submitted
16:31:49.048 [main] INFO my.pack.RWLockTest - 30 was submitted
16:31:49.048 [main] INFO my.pack.RWLockTest - 31 was submitted
16:31:49.049 [main] INFO my.pack.RWLockTest - 32 was submitted
16:31:49.049 [main] INFO my.pack.RWLockTest - 33 was submitted
16:31:54.047 [Thread-7] INFO my.pack.RWLockTest - Read by 19 finished
16:31:54.048 [Thread-6] INFO my.pack.RWLockTest - Read by 18 finished
16:31:54.047 [Thread-5] INFO my.pack.RWLockTest - Read by 17 finished
16:31:54.049 [Thread-2] INFO my.pack.RWLockTest - Read by 14 finished
16:31:54.051 [Thread-8] INFO my.pack.RWLockTest - Read by 20 finished
16:31:54.047 [Thread-1] INFO my.pack.RWLockTest - Read by 13 finished
16:31:54.050 [Thread-9] INFO my.pack.RWLockTest - Read by 21 finished
16:31:54.049 [Thread-4] INFO my.pack.RWLockTest - Read by 16 finished
16:31:54.049 [Thread-3] INFO my.pack.RWLockTest - Read by 15 finished
16:31:54.049 [Thread-0] INFO my.pack.RWLockTest - Read by 12 finished
16:31:54.057 [Thread-10] INFO my.pack.RWLockTest - !!!!!!!!!!Write by 22 started!!!!!!!!!!!!
16:31:57.057 [Thread-10] INFO my.pack.RWLockTest - !!!!!!!!!!Write by 22 finished!!!!!!!!
16:31:57.058 [Thread-11] INFO my.pack.RWLockTest - !!!!!!!!!!Write by 23 started!!!!!!!!!!!!
16:32:00.060 [Thread-11] INFO my.pack.RWLockTest - !!!!!!!!!!Write by 23 finished!!!!!!!!
16:32:00.061 [Thread-13] INFO my.pack.RWLockTest - Read by 25 started
16:32:00.061 [Thread-14] INFO my.pack.RWLockTest - Read by 26 started
16:32:00.061 [Thread-12] INFO my.pack.RWLockTest - Read by 24 started
16:32:00.061 [Thread-15] INFO my.pack.RWLockTest - Read by 27 started
16:32:00.061 [Thread-17] INFO my.pack.RWLockTest - Read by 29 started
16:32:00.062 [Thread-19] INFO my.pack.RWLockTest - Read by 31 started
16:32:00.062 [Thread-18] INFO my.pack.RWLockTest - Read by 30 started
16:32:00.061 [Thread-16] INFO my.pack.RWLockTest - Read by 28 started
16:32:00.062 [Thread-20] INFO my.pack.RWLockTest - Read by 32 started
16:32:00.062 [Thread-21] INFO my.pack.RWLockTest - Read by 33 started
16:32:05.060 [Thread-12] INFO my.pack.RWLockTest - Read by 24 finished
16:32:05.060 [Thread-15] INFO my.pack.RWLockTest - Read by 27 finished
16:32:05.060 [Thread-13] INFO my.pack.RWLockTest - Read by 25 finished
16:32:05.060 [Thread-17] INFO my.pack.RWLockTest - Read by 29 finished
16:32:05.060 [Thread-14] INFO my.pack.RWLockTest - Read by 26 finished
16:32:05.062 [Thread-21] INFO my.pack.RWLockTest - Read by 33 finished
16:32:05.062 [Thread-16] INFO my.pack.RWLockTest - Read by 28 finished
16:32:05.062 [Thread-19] INFO my.pack.RWLockTest - Read by 31 finished
16:32:05.062 [Thread-18] INFO my.pack.RWLockTest - Read by 30 finished
16:32:05.062 [Thread-20] INFO my.pack.RWLockTest - Read by 32 finished

什么意思?

如您所见,我执行以下操作:

运行 10 个线程
  1. 进行读取。所有 10 个线程都可以并行工作。每次读取需要 5 秒
  2. 然后我开始 2 个编写器(1 个写入需要 3 秒(
  3. 然后我开始10个阅读器

如您所见,提交第一次写入(16:31:49.037 [main] INFO my.pack.RWLockTest - !!!!!!!!!!!!!!!WRITER_1 WAS STARTED!!!!!!!!!!!!!!!(和实际声明((获得锁(16:31:54.057 [Thread-10] INFO my.pack.RWLockTest - !!!!!!!!!!Write by 22 started!!!!!!!!!!!!(之间的延迟为5秒。这是一个阅读的时代。

您还可以看到,在提交 2 个编写器线程后,我们提交了 10 个 ID 从24 到 33的阅读器线程,并且所有这些线程实际上是在编写线程完成其工作后启动(获取锁(的。

因此,ID从 24 到 33 的读取器线程在16:31:49提交,此时 readlock 被前 10 个读者获取,看起来他们也能够获得 readlock,但看起来ReentrantReadWriteLock内部有一些东西可以阻止它以避免写入器饥饿。最终,第二组读者只能在16:32:00(提交后6秒(获得锁定

我不知道它是否可以保证,但从我的测试来看,它总是以这种方式工作。所以我们有一些作家优先于读者,尽管java文档说:

此类不强加读取器或编写器首选项排序 锁定访问

最终的答案总是在代码中,所以让我们看看那里。

这是构造函数(注意:默认构造函数调用此构造函数,fair设置为false(

public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}

因此,唯一的区别是sync属性是包含FairSync实例还是NonfairSync实例。 这些实现有何不同?

下面是来自FairSync类的writerShouldBlock方法的代码:

final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}

这意味着"如果有一行",那么编写器会阻塞并进入该行(队列(。 但是,这与NonfairSync类的实现形成鲜明对比,后者是:

final boolean writerShouldBlock() {
return false;
}

这明确表明non fair mode作家如何获得优先于读者。

关于作家饥饿的最后一点评论。non fair mode,这是在配套方法的实现中实现的:readerShouldBlockNonfairSync类中代码的注释指出:

final boolean readerShouldBlock() {
/* As a heuristic to avoid indefinite writer starvation,
* block if the thread that momentarily appears to be head
* of queue, if one exists, is a waiting writer.  This is
* only a probabilistic effect since a new reader will not
* block if there is a waiting writer behind other enabled
* readers that have not yet drained from the queue.
*/

最新更新