条件vs等待通知机制

时间:2012-05-01 08:58:43

标签: java multithreading concurrency

使用Condition接口/实现优于传统的等待通知机制有什么优势?在这里,我引用Doug Lea撰写的评论:

  

条件将Object监视器方法(wait,notify和notifyAll)分解为不同的对象,通过将它们与使用任意Lock实现相结合,实现每个对象具有多个等待集的效果。如果Lock替换了synchronized方法和语句的使用,则Condition将替换Object监视方法的使用。

我认为这是一种更加面向对象的实现等待/通知机制的方式。但是,前者是否有合理的优势?

6 个答案:

答案 0 :(得分:30)

最大的问题是新开发人员的wait / notify容易出错。主要问题是不知道如何正确处理它们可能导致模糊的错误。

  • 如果在wait()之前调用notify(),则会丢失。
  • 有时可能不清楚是否在同一个对象上调用了notify()和wait()。
  • wait / notify中没有任何内容需要更改状态,但在大多数情况下这是必需的。
  • wait()可以虚假地返回

Condition将此功能包装到一个专用组件中,但它的行为大致相同。

有一个问题是关于在此之前几分钟发布的等待/ nofity以及许多,更多Search [java]+wait+notify

答案 1 :(得分:29)

使用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();
    }       
}

答案 2 :(得分:19)

如上所述,条件接口有很多优点,其中一些重要内容如下:

条件界面附带两个额外方法

1)boolean awaitUntil(Date deadline)抛出InterruptedException:           导致当前线程等待,直到发出信号或中断,或者指定的截止时间结束。

2)awaitUninterruptibly():           导致当前线程等待直到发出信号。

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

上述两种方法在对象类的默认监视器中不存在,在某些情况下我们要设置线程等待的截止时间,然后我们可以通过Condition接口来完成。

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

有关更多信息,请参阅条件接口Java文档:

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/locks/Condition.html#awaitUntil%28java.util.Date%29

答案 3 :(得分:1)

@AfterWorkGuinness

  

在发出信号

之前,您是否应该设置usedData = true / false

信号代码一直到锁定块结束并释放它,所以命令无关紧要

答案 4 :(得分:1)

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

使用wait / notify,如果线程正在等待不同的东西(常见的例子是固定大小的阻塞队列,有些线程将事物放入队列并在队列满时阻塞,而其他线程从队列中获取队列为空时队列和阻塞)然后,如果您使用notify,导致调度程序从等待集中选择一个线程进行通知,则可能存在所选线程无法针对特定情况通知的极端情况。例如,队列将通知向队列添加内容,但如果所选线程是生产者并且队列已满,则它无法对该通知执行操作,您宁愿将其发送给使用者。使用内部锁定时,您必须使用notifyAll以确保通知不会丢失。

但是,每次调用时,notifyAll都会产生流失,每个线程都会唤醒并争夺锁定,但只有一个可以取得进展。其他线程都在争夺锁定,直到一次一个,他们可以获得锁定,并且很可能会回到等待状态。它产生了很多争用,没有多少好处,最好是能够使用notify并知道只有一个线程被通知,其中通知与该线程相关。

这就是要等待单独条件的地方是一个很大的改进。队列可以在某个条件下调用信号,并知道它只会唤醒一个线程,该线程专门等待该条件。

API doc for Condition有一个代码示例,显示了对有界缓冲区使用多个条件,它说:

  

我们希望继续等待put线程并将线程放在单独的等待集中,以便我们可以使用优化,只有当缓冲区中的项或空间可用时才通知单个线程。

答案 5 :(得分:0)

除了其他公认的答案外-由于Condition与Lock对象相关联,因此您可以在类中具有任意的Lock对象集(重写,读取,写入),并具有与之关联的特定条件。然后,您可以使用这些条件集根据实现语义来同步类的不同部分。与等待通知imo相比,这提供了更大的灵活性和明确的行为