我有以下情况。几个线程正在等待相同的条件。通知后,所有人都应该停止等待,更改标志并返回对象:
public Object getObject(){
lock.lock();
try {
while (check)){
condition.await();
}
return returnObjectAndSetCheckToFalse();
} finally {
lock.unlock();
}
}
但是这段代码不起作用,因为更快的线程可能会将check标志更改为false,而第二个更慢的线程将再次阻塞。 可能有一个逻辑,即两个等待线程都将被唤醒,它们都将check flag设置为false,并返回对象? 或许它是矛盾的?
最简单的方法是将wait更改为if语句,但这很容易受到虚假唤醒的攻击。</ p>
答案 0 :(得分:5)
您可以使用CountDownLatch
或CyclicBarrier
。
使用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
,是纯随机的。你应该减少不确定性,而不是增加它。