如何唤醒等待相同条件的所有线程?

时间:2013-09-26 13:18:32

标签: java multithreading concurrency wait notify

我有以下情况。几个线程正在等待相同的条件。通知后,所有人都应该停止等待,更改标志并返回对象:

 public Object getObject(){
    lock.lock();
    try {
        while (check)){
            condition.await();
        }

        return returnObjectAndSetCheckToFalse();
    } finally {
        lock.unlock();
    }
}

但是这段代码不起作用,因为更快的线程可能会将check标志更改为false,而第二个更慢的线程将再次阻塞。 可能有一个逻辑,即两个等待线程都将被唤醒,它们都将check flag设置为false,并返回对象?  或许它是矛盾的?

最简单的方法是将wait更改为if语句,但这很容易受到虚假唤醒的攻击。<​​/ p>

5 个答案:

答案 0 :(得分:5)

您可以使用CountDownLatchCyclicBarrier

使用Future也是可能的,FutureTask更具体。它有一个Conveniance方法get(),可用于阻止代码执行,直到Future完成其工作,从而满足您的要求。

您还可以实现自己的障碍,在循环中执行wait()直到满足某个条件。实现该条件将触发notifyAll(),循环将完成并且所有线程都可以继续。但这将重新发明轮子。

答案 1 :(得分:1)

我认为您正在努力实现,使用Future s:

完成
ExecutorService executor = Executors.newCachedThreadPool();

// producer
final Future<String> producer = executor.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        Thread.sleep(5000);
        return "done";
    }
});

// consumers
for (int i = 0; i < 3; i++) {
    final int _i = i;
    executor.submit(new Runnable() {
        @Override
        public void run() {
            System.out.println("Consumer "+_i+" starts.");
            try {
                String value = producer.get();
                System.out.println("Consumer "+_i+" ends: "+value);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    });
}

如果你运行这个,你应该看到所有的消费者线程打印出他们的开始消息,然后暂停,然后消费者线程打印出他们已经完成。显然你必须将产生getObject()值的任何内容改为Callable,但我敢打赌这会简化代码,因为现在它将按程序结构化而不是存储结果共享变量中的计算。我也比使用手动锁定的任何代码更自信它的线程安全。

答案 2 :(得分:1)

一种方法是使用wait()代替condition.await()。然后使用notifyAll()唤醒线程。

理想情况下,您将继续使用导致线程休眠的条件对象,并调用方法signalAll()来唤醒所有线程。

在你的代码中我只想添加:

public Object getObject(){
lock.lock();
try {
    while (check)){
        condition.await();
    }
        condition.signalAll();
    return returnObjectAndSetCheckToFalse();
} finally {
    lock.unlock();
}

}

我甚至会考虑在returnObjectAndSetCheckToFalse()方法中而不是在return语句之前使用condition.signalAll()的可能性。

答案 3 :(得分:1)

据我所知,如果你的condition.await()返回,你需要从所有线程中的方法体返回。 这个丑陋的解决方案应该会有所帮助,尽管我认为有更好的解决方法:

public Object getObject() {
  lock.lock();
  try {
    int localstate = this.state;

    while (check && localstate == this.state)) {
      condition.await(); // all threads that are waiting here have the same state
    }

    if (!check) {
      this.state++; // first thread will change state thus making other threads ignore the 'check' value
    }

    return returnObjectAndSetCheckToFalse();
  } finally {
    lock.unlock();
  }
}

答案 4 :(得分:0)

确实这是矛盾的。你想要实现的是有问题的。您希望在条件上等待的线程应该获得结果并继续,但是在通知之后调用getObject的线程不会。至少,这是不公平的。该线程是否设法在通知之前调用getObject,是纯随机的。你应该减少不确定性,而不是增加它。