条件与等待通知机制

与传统的等待通知机制相比,使用条件接口/实现的优势是什么?这里我引用 Doug Lea 的评论:

条件将 Object 监视器方法(wait、 tification 和 notifyAll)分解成不同的对象,通过将它们与任意 Lock 实现的使用相结合,给出了每个对象具有多个等待集的效果。在使用 Lock 替换同步方法和语句的情况下,条件替换 Object 监视器方法的使用。

我发现这是一种更加面向对象的实现等待/通知机制的方法。但是,与前者相比,有什么明显的优势吗?

37048 次浏览

最大的问题是等待/通知对于新开发人员来说很容易出错。主要问题是不知道如何正确处理它们会导致模糊的错误。

  • 如果在 wait ()之前调用 tification () ,则丢失。
  • 如果对同一个对象调用 tification ()和 wait () ,有时可能不清楚。
  • 等待/通知中没有需要更改状态的内容,但是在大多数情况下需要更改状态。
  • 等待()可以虚假地返回

条件将此功能封装到一个专用组件中,但其行为基本相同。

有一个问题,关于等待/通知张贴分钟之前,这一个和许多,许多更多的 搜索[ 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();
}
}

关于 条件接口,有许多如上所述的 优势,其中一些重要的如下:

条件接口 带有 两个额外的 方法,它们是:

1)布尔值等待直到(截止日期)抛出 InterruptedException: 使当前线程等待,直到发出信号或中断,或指定的最后期限过期。

2)等待不间断() : 使当前线程等待,直到发出信号。

如果当前线程的中断状态在进入此方法时被设置,或者在等待时被中断,则它将继续等待,直到发出信号。当它最终从此方法返回时,它的中断状态仍将被设置。

上面两个方法在对象类中的默认监视器中不存在,在某些情况下,我们需要设置线程等待的最后期限,然后我们可以通过条件接口来做到这一点。

在某些情况下,我们不希望线程被中断,并希望当前线程等待,直到它发出信号,然后我们可以去等待条件接口中出现的不可中断方法。

更多信息条件接口 Java 文档:

Http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/condition.html#awaituntil%28java.util 日期% 29

具体说明为什么拥有多个等待集是一个优势:

如果有不同的线程在等待通知(常见的例子是一个固定大小的阻塞队列,一些线程在队列中放置东西,当队列满时阻塞,其他线程从队列中获取,当队列为空时阻塞) ,那么如果你使用通知,导致调度程序从等待集中选择一个线程来通知,你可能会遇到一些角落情况,选择的线程对于特定的情况不感兴趣。例如,队列将通知向队列添加某些内容,但是如果选择的线程是生产者且队列已满,那么它就不能对该通知进行操作,而您宁愿将该通知发送给使用者。对于内部锁定,必须使用 notifyAll,以确保通知不会丢失。

但是 notifyAll 在每次调用时都会引起混乱,其中每个线程都会被唤醒并争夺锁,但是只有一个线程可以取得进展。其他线程都围绕锁展开竞争,直到一次一个线程可以获得锁,并且最有可能回到等待状态。它产生了大量的争用,但没有多少好处,最好是能够使用通知,并且知道只有一个线程被通知,其中的通知与该线程相关。

这种情况下,使用单独的条件等待是一个很大的改进。队列可以对条件调用信号,并且知道它将只唤醒一个线程,其中该线程正在专门等待条件。

空气污染指数文件的一个代码示例显示了对有界缓冲区使用多个条件,它说:

我们希望继续等待 put 线程,并在单独的等待集中获取线程,这样我们就可以使用这样的优化: 当缓冲区中的项或空间变得可用时,只通知单个线程。

除了其他被广泛接受的答案之外——因为条件与 Lock 对象相关联,所以在类中可以有任意组的 Lock 对象(重写、读取、写入) ,并且具有与之相关联的特定条件。然后,您可以使用这些条件集来根据实现语义同步类的不同部分。这给予了更多的灵活性和明确的行为,然后等待-通知模拟