Java 条件vs等待通知机制

Java 条件vs等待通知机制,java,multithreading,concurrency,Java,Multithreading,Concurrency,与传统的等待通知机制相比,使用条件接口/实现的优势是什么?这里我引用Doug Lea的评论: 条件将对象监视器方法(wait、notify和notifyAll)分解为不同的对象,通过将它们与任意锁实现的使用相结合,为每个对象提供多个等待集的效果。如果锁取代了同步方法和语句的使用,则条件取代了对象监视器方法的使用 我认为这是一种更面向对象的实现等待/通知机制的方法。但与前者相比,是否有合理的优势 最大的问题是等待/通知对于新开发人员来说很容易出错。主要的问题是不知道如何正确处理它们,结果可能是模糊

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

条件将对象监视器方法(wait、notify和notifyAll)分解为不同的对象,通过将它们与任意锁实现的使用相结合,为每个对象提供多个等待集的效果。如果锁取代了同步方法和语句的使用,则条件取代了对象监视器方法的使用


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

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

  • 如果在wait()之前调用notify(),则它将丢失
  • 有时不清楚是否对同一对象调用了notify()和wait()
  • wait/notify中没有需要状态更改的内容,但在大多数情况下都需要这样做
  • wait()可能会错误返回
Condition将此功能包装到专用组件中,但其行为基本相同


当您使用
条件:wait()/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)布尔等待直到(日期截止日期)抛出中断异常: 使当前线程等待,直到发出信号或被中断,或者指定的截止日期过去

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

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

上述两种方法不存在于对象类中的默认监视器中,在某些情况下,我们希望设置线程等待的截止日期,然后我们可以通过条件接口来完成

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

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


为了明确说明为什么拥有多个等待集是一种优势:

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

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

在这方面,有单独的条件等待是一个很大的改进。队列可以在某个条件下调用信号,并知道它将只唤醒一个线程,而该线程正在特定地等待该条件

有一个代码示例显示如何对有界缓冲区使用多个条件,它说:

我们希望保持等待,在单独的等待集中放置线程和获取线程,这样我们就可以在缓冲区中的项目或空间可用时使用一次只通知单个线程的优化


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

您可以拥有多个共享同一锁的条件。e、 g.wait/notify只有一个“等待集”,即您只能通知任何服务员,而不能通知特定的群体。我不确定除第三点外,条件对其他方面有何帮助。如果在wait()之前调用,则对信号的调用将丢失,wait仍然可以提前返回,接口中没有任何内容表明状态必须更改。@Peter Lawrey如果在wait()之前调用notify(),则该调用将丢失。我不明白。这是什么意思?@gstackoverflow notify()只通知等待的线程。如果在notify()之后等待(),等待者不知道您之前调用了notify。@PeterLawrey“条件是有状态的。”