Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Producer/Consumer in Java. Why do we need two conditions?

I've read about the problem on https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html.

This is what the documentation says about the two conditions:

We would like to keep waiting put threads and take threads in separate wait-sets so that we can use the optimization of only notifying a single thread at a time when items or spaces become available in the buffer. This can be achieved using two Condition instances.

I could notify a single thread using one condition since signal() wakes up one thread:

class BoundedBuffer2 {
    final Lock lock = new ReentrantLock();
    final Condition condition  = lock.newCondition();

    final Object[] items = new Object[100];
    int putptr, takeptr, count;

    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length)
                condition.await();
            items[putptr] = x;
            if (++putptr == items.length) putptr = 0;
            ++count;
            condition.signal();
        } finally {
            lock.unlock();
        }
    }

    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0)
                condition.await();
            Object x = items[takeptr];
            if (++takeptr == items.length) takeptr = 0;
            --count;
            condition.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }
}

Why do we need two conditions?

like image 596
Maksim Dmitriev Avatar asked Apr 11 '16 05:04

Maksim Dmitriev


People also ask

What is race condition in producer-consumer?

A race condition is a situation in which two or more threads or processes are reading or writing some shared data, and the final result depends on the timing of how the threads are scheduled. Race conditions can lead to unpredictable results and subtle program bugs.

What is producer and consumer problem in Java?

In computing, the producer-consumer problem (also known as the bounded-buffer problem) is a classic example of a multi-process synchronization problem. The problem describes two processes, the producer and the consumer, which share a common, fixed-size buffer used as a queue.

What are condition variables in Java?

Conditions (also known as condition queues or condition variables) provide a means for one thread to suspend execution (to "wait") until notified by another thread that some state condition may now be true.

What is Producer-consumer relationship in Java?

Producer and Consumer are two separate processes. Both processes share a common buffer or queue. The producer continuously produces certain data and pushes it onto the buffer, whereas the consumer consumes those data from the buffer.


2 Answers

Say your buffer was empty and two threads were waiting on take, blocked on

condition.await();

Then a thread calls put. This will signal the Condition and wake up one of the take threads. That take thread will subsequently also signal the Condition, waking up the other take thread which will loop back in blocking state since there is nothing for it to take.

That last signal was unnecessary. That second take thread should never have been woken up since nothing was available.

The two condition algorithm allows you to notify only those threads that are relevant.

like image 132
Savior Avatar answered Nov 03 '22 12:11

Savior


The reason for two conditions is to differentiate between threads which are producing items into the buffer and threads which are consuming items from the buffer.

  • ProducingThread is bothered about notFull condition and is responsible for notEmpty condition, meaning that once it produces something, it has to raise the flag and say, 'hey, i have produced something, so it is not empty now.'

  • ConsumingThread is bothered about notEmpty condition and is responsible for notFull condition , meaning that once it consumes something, it has to raise the flag and say, 'hey, i have consumed something, so it is not full now.'

By having this kind of separation of threads into two different wait-sets, we can alert one thread at a time - either a producer thread from producerThread set or a consumer thread from consumerThread set. By having a single condition, we might have a producer thread to alert another producer thread, which is not necessary or relevant.

If we have a single condition, which is used by both threads, we will not be able to differentiate which threads to give chance logically, for example, 3 producer threads & 2 consumer threads are waiting for their chance - either to add/remove item. If one of the consuming thread finishes its work, suppose it signals, then it is possible that another consuming thread might get woken up, which would not be our objective.

We might have different usecases of Producer/Consumer problem-

  1. Interleaving of Tasks - We can have tasks interleaving - for example - one consume, one produce, one consume, one produce etc.
  2. Execute only relevant tasks - Producing threads need to be woken up only if producing task is necessary/possible and similarly Consuming threads need to be woken up only if consuming task is necessary/possible, otherwise the logic would become irrelevant/broken.
like image 22
a3.14_Infinity Avatar answered Nov 03 '22 13:11

a3.14_Infinity