3

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

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

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

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

最简单的方法是将等待更改为 if 语句,但这很容易受到虚假唤醒的影响。

4

5 回答 5

5

您可以使用CountDownLatchCyclicBarrier.

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

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

于 2013-09-26T13:25:47.347 回答
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() 的可能性。

于 2013-09-26T13:44:50.127 回答
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();
  }
}
于 2013-09-26T14:49:23.743 回答
1

我认为您正在尝试实现,使用Futures 完成:

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()为 aCallable但我敢打赌,这将简化代码,因为现在它将按程序进行结构化,而不是将计算结果存储在共享变量中。与使用手动锁定的任何代码相比,我也更有信心它是线程安全的。

于 2013-09-26T13:38:12.927 回答
0

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

于 2013-09-26T18:55:10.637 回答