Pergunta

I have a following scenario. Several threads are waiting on the same condition. And when are notified, all should stop waiting, change flag and return object:

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

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

however this code does not work, since faster thread could change check flag to false, and second slower thread will block again. It is possible to have a logic that both waiting threads will be awaken, they both will set check flag to false, and return object? Or maybe it is contradictory?

The easiest way would be to change wait to if statement, however this would be vulnerable to spurious wakeup.

Foi útil?

Solução 2

As I understand you need to return from the method body in all threads if your condition.await() returns. This ugly solution should help although I think there's a better way to solve this:

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();
  }
}

Outras dicas

You could use CountDownLatch or a CyclicBarrier.

Using a Future is also a possibility, a FutureTask to be more specific. It has a conveniance method get() which can be used to block code execution until the Future has completed its job, thus fulfilling your requirements.

You could also implement your own Barrier which would do wait() in a loop until a certain condition has been met. Fulfilling that condition would trigger notifyAll(), loop would finish and all threads could continue. But that would be reinventing the wheel.

What I think is you're trying to achieve, done using 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();
            }
        }
    });
}

If you run this, you should see all the consumer threads printing out their starting message, then a pause, then the consumer threads print out they're done. Obviously you'd have to change whatever is producing the value of getObject() into a Callable but I'd bet good money this will simplify the code since now it'll be structured procedurally instead of storing the result of a computation in a shared variable. I'm also more confident it's thread safe than of any code using manual locking.

One way of doing it is using wait() instead of condition.await(). Then use notifyAll() to wake up the threads.

Ideally, you would continue using the condition object that causes the thread to sleep and invoke the method signalAll() to wake up all the threads.

In you code I would just add:

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

}

I would even look at the possibility of using the condition.signalAll() inside the returnObjectAndSetCheckToFalse() method instead of before the return statement.

Indeed it it is contradictory. What you want to achieve is problematic. You want threads waiting on the condition should get result and continue, but a thread that invokes getObject just after notification would not. At least, it is unfair. Whether that thread manages to call getObject before notification or not, is pure random thing. You should decrease indeterminism, not to increase it.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top