最近我读到《多处理器编程的艺术》第8章中的一些例子,关于">监视器和阻塞同步",这些例子使用Condition
对象的signalAll()
,而不获取与该Condition
相关的锁。
令人惊讶的是,我在本书的勘误表中没有找到这些示例的任何修复方法。此外,他们建议对图8.12的FifoReadWriteLock
的例子进行更正,但他们继续使用没有锁的signalAll()
。这让我感到不安,我试图找到有关这些示例的其他考虑因素,以了解以这种方式编写这些 Java 示例的原因。
例如,"读写互斥锁如何工作?"问题的答案显示了实现FifoReadWriteLock
的相同示例,它将writeUnlock()
实现为:
void writeUnlock() {
writer = false;
condition.signalAll();
}
关于没有锁采集,您可以阅读两个不同的原因:
- 仅将其用作伪代码
- 条件变量的某些实现不需要保持锁发出信号。
很难接受第一个论点,因为这本书使用了Java中的例子,并明确指出:
本书使用了Java编程语言。
关于第二点,我知道Java API处于java.util.concurrent.locks.Condition
状态signal()
方法:
在调用此方法时,实现可能(并且通常确实(要求当前线程持有与此
Condition
关联的锁。
如果仅">实现可能",则意味着它不是强制性的。然而,据我所知,我没有发现任何不符合此要求的实现。所以我想知道哪些 Java Condition
实现不需要当前线程来保持锁?
JDK中有任何Condition
实现允许在不同时拥有显示器的情况下等待或发出信号。
实际上,所有java.util.concurrent
类都依赖于AbstractQueuedSynchronizer
,它为其提供的条件变量建立了与内置监视方法相同的合约wait()
/notify()
/notifyAll()
,即它需要拥有内部锁才能允许调用await()
/signal()
/signalAll()
。
如果你尝试一个简单的例子 使用建议的FifoReadWriteLock
,你会发现它writeUnlock()
方法会喷出大量的IllegalMonitorStateExceptions
。如果应用其他方法中的锁定尝试最终方法,则这些异常将消失。
虽然确实拥有监视器并不是绝对需要等待或发出信号,但通常是更可取的方法,因为它可以让您免于不雅的条件读取,它不应该太昂贵,因为同一监视器的内部等待集之间的切换仍然可以相当有效地完成,并且因为大多数情况下您需要它来发送信号和调度,而不仅仅是发出信号。