Write the Producer/Consumer problem using wait and notify.

The Producer-Consumer problem is a classic synchronization scenario where one or more producer threads generate data and place it into a shared buffer, while consumer threads remove and process that data. The wait() and notify()/notifyAll() methods are used to coordinate thread execution, ensuring that producers do not add data when the buffer is full and consumers do not remove data when the buffer is empty.

Key Points: • wait() releases the monitor lock and suspends the thread until another thread notifies it. • notify() or notifyAll() wakes up waiting threads when the buffer state changes. • Proper synchronization prevents race conditions and ensures safe communication between producer and consumer threads.

Example: Consider an order-processing system where producers create customer orders and place them into a queue, while consumers process those orders. If the queue becomes full, producers wait. If the queue becomes empty, consumers wait until new orders arrive.

Code Example:

import java.util.LinkedList;
import java.util.Queue;

public class ProducerConsumer {

    private final Queue<Integer> buffer =
            new LinkedList<>();

    private final int CAPACITY = 5;

    public void produce() throws InterruptedException {

        int value = 1;

        while (true) {

            synchronized (this) {

                while (buffer.size() == CAPACITY) {
                    wait();
                }

                System.out.println(
                        "Produced: " + value);

                buffer.add(value++);

                notifyAll();
            }

            Thread.sleep(1000);
        }
    }

    public void consume() throws InterruptedException {

        while (true) {

            synchronized (this) {

                while (buffer.isEmpty()) {
                    wait();
                }

                int value = buffer.poll();

                System.out.println(
                        "Consumed: " + value);

                notifyAll();
            }

            Thread.sleep(1000);
        }
    }

    public static void main(String[] args) {

        ProducerConsumer pc =
                new ProducerConsumer();

        Thread producer =
                new Thread(() -> {

                    try {
                        pc.produce();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });

        Thread consumer =
                new Thread(() -> {

                    try {
                        pc.consume();
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });

        producer.start();
        consumer.start();
    }
}

Interview Tip: A concise interview answer is: The Producer-Consumer problem demonstrates thread coordination using wait() and notify()/notifyAll(). Producers wait when the shared buffer is full, consumers wait when it is empty, and synchronization ensures safe access to shared resources without race conditions.