条件vs等待通知机制

use*_*erx 65 java concurrency multithreading

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

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

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

Pet*_*rey 31

最大的问题是新开发人员容易出错等待/通知.主要问题是不知道如何正确处理它们可能导致模糊的bug.

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

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

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

  • 除了第三点,我不确定条件如何帮助.如果在等待之前调用,则信号调用将丢失,等待仍然可以提前返回,接口中的任何内容都不会指示状态必须更改. (4认同)
  • @PeterLawrey _"一个条件是有状态的并且记住状态变化直到它被消耗"_我相信这是不正确的:**如果在await()**之前调用,条件的信号()也会丢失,就像对象的通知一样如果在wait()之前调用,则()丢失.我建议在答案中添加一些澄清,条件对第1点和第4点没有帮助(但是它们更"通用"的意思是"更好" - 另见[此答案](http:// stackoverflow的.com /一个/396255分之11272191)) (4认同)
  • 您可以拥有共享同一锁的多个条件.例如http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html等待/通知只有一个"等待集",即你只能通知任何服务员,而不是特定群体. (3认同)
  • @gstackoverflow notify()仅通知等待的线程.如果在notify()之后wait(),则等待不知道您之前调用了notify. (2认同)

kas*_*ere 30

使用时,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();
    }       
}
Run Code Online (Sandbox Code Playgroud)

  • 当您使用 wait()/notify() 时,您无法决定通知哪些等待对象/线程:您可以决定。 (2认同)

小智 21

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

条件接口附带两个额外的方法:

1)boolean awaitUntil(Date deadline)throws 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

  • 如果系统时钟发生变化,awaitUntil(Date deadline)的行为会发生变化,而不是等待(long timeInMilliseconds) (2认同)

Nat*_*hes 5

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

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

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

这就是拥有单独的条件来等待是一个很大的改进。队列可以在某个条件上调用信号,并知道它将仅唤醒一个线程,该线程专门等待该条件。

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

我们希望继续等待放置线程并将线程放入单独的等待集中,以便我们可以使用优化,即当缓冲区中的项目或空间可用时,一次仅通知单个线程。