与传统的等待通知机制相比,使用Condition接口/实现的优点是什么?在这里,我引用道格·李(Doug Lea)的评论:
条件将对象监视方法(wait,notify和notifyAll)分解为不同的对象,从而通过与任意Lock实现结合使用,从而使每个对象具有多个等待集。如果Lock替换了同步方法和语句的使用,而Condition替换了Object监视方法的使用。
我看到这是实现等待/通知机制的一种更面向对象的方式。但是,与前者相比有没有明显的优势?
Answers:
如上所述,关于条件接口有许多优点,一些重要的如下:
条件接口附带两个额外的方法:
1)布尔值awaitUntil(日期截止时间)引发InterruptedException: 导致当前线程等待,直到发出信号或被中断或指定的截止时间过去为止。
2)awaitUninterruptible(): 使当前线程等待,直到发出信号为止。
如果当前线程进入此方法时设置了中断状态,或者在等待时被中断,它将继续等待,直到发出信号为止。当它最终从该方法返回时,其中断状态仍将被设置。
以上两种方法在对象类的默认监视器中不存在,在某些情况下,我们希望设置线程等待的截止日期,然后我们可以通过Condition接口实现此任务。
在某些情况下,我们不希望线程被中断,而希望当前线程等待,直到收到信号为止,然后我们可以使用条件接口中存在的awaitUninterruptible方法。
有关更多信息,条件接口Java文档:
使用时,Condition: await()/signal()
您可以区分哪个对象或一组对象/线程获得特定信号。这是一个简短的示例,其中一些线程(生产者)将获得isEmpty
信号,而消费者将获得isFull
信号:
private volatile boolean usedData = true;//mutex for data
private final Lock lock = new ReentrantLock();
private final Condition isEmpty = lock.newCondition();
private final Condition isFull = lock.newCondition();
public void setData(int data) throws InterruptedException {
lock.lock();
try {
while(!usedData) {//wait for data to be used
isEmpty.await();
}
this.data = data;
isFull.signal();//broadcast that the data is now full.
usedData = false;//tell others I created new data.
}finally {
lock.unlock();//interrupt or not, release lock
}
}
public void getData() throws InterruptedException{
lock.lock();
try {
while(usedData) {//usedData is lingo for empty
isFull.await();
}
isEmpty.signal();//tell the producers to produce some more.
usedData = true;//tell others I have used the data.
}finally {//interrupted or not, always release lock
lock.unlock();
}
}
最大的问题是等待/通知对于新开发人员来说容易出错。主要问题是不知道如何正确处理它们可能会导致模糊的错误。
Condition将此功能包装到专用组件中,但是其行为几乎相同。
在此之前还有一个问题,关于等待/无效发布的时间,还有很多很多。搜索[java] + wait + notify
要专门解决为什么拥有多个等待集是一个优势:
使用wait / notify通知线程是否正在等待不同的事物(常见示例是固定大小的阻塞队列,其中一些线程将事物放入队列中,并在队列已满时阻塞,而其他线程从队列中取出并阻塞当队列为空时),则如果您使用notify,从而导致调度程序从等待集中选择一个线程进行通知,则可能会遇到一些极端情况,即在特定情况下所选线程不希望被通知。例如,队列将通知要向队列中添加某些内容,但是如果所选线程是生产者并且队列已满,则它无法对该通知采取行动,而您宁愿交给消费者。使用内部锁定,您必须使用notifyAll,以确保通知不会丢失。
但是notifyAll每次调用都会引起混乱,每个线程都醒来并争夺锁,但是只有一个线程可以取得进展。其他线程都争先恐后地争夺锁,直到一次获得一个锁,并且很可能回到等待状态。它会产生很多争用,而不会带来太大的好处,因此最好能够使用notify并知道仅通知了一个线程,而该通知与该线程相关。
在这里,单独等待条件是一个很大的改进。队列可以根据条件调用信号,并知道它将仅唤醒一个线程,而该线程专门在等待该条件。
Condition的API文档有一个代码示例,该代码示例显示了如何对有界缓冲区使用多个条件,它说:
我们希望继续等待放置线程,并在单独的等待集中获取线程,以便我们可以使用仅在缓冲区中的项目或空间可用时才通知单个线程的优化。