Java.Multithreading.Example with consumer and producer

Let’s compare wait()/notify() (used with synchronized) and Condition (used with ReentrantLock) — both are used for thread coordination, but Condition gives you more flexibility.

We’ll walk through:

  1. A wait()/notify() example
  2. A Condition example doing the same thing

Scenario: We have a producer and consumer. The producer sets a flag when data is ready; the consumer waits until that happens.


🧵 1. wait()/notify() Example with synchronized

class SharedData {
    private boolean ready = false;

    public synchronized void waitForData() {
        while (!ready) {
            try {
                wait(); // 💤 releases monitor and waits
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        System.out.println("Consumer: data is ready!");
    }

    public synchronized void produceData() {
        ready = true;
        notify(); // 🚨 wakes one waiting thread
        System.out.println("Producer: data produced!");
    }
}

public class Main {
    public static void main(String[] args) {
        SharedData data = new SharedData();

        Thread consumer = new Thread(data::waitForData);
        Thread producer = new Thread(() -> {
            try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
            data.produceData();
        });

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

✅ Output:

Producer: data produced!
Consumer: data is ready!

🔐 2. Same Logic Using Condition + ReentrantLock

import java.util.concurrent.locks.*;

class SharedDataWithLock {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean ready = false;

    public void waitForData() {
        lock.lock();
        try {
            while (!ready) {
                condition.await(); // 💤 releases lock and waits
            }
            System.out.println("Consumer: data is ready!");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

    public void produceData() {
        lock.lock();
        try {
            ready = true;
            condition.signal(); // 🚨 wakes one waiting thread
            System.out.println("Producer: data produced!");
        } finally {
            lock.unlock();
        }
    }
}

public class Main {
    public static void main(String[] args) {
        SharedDataWithLock data = new SharedDataWithLock();

        Thread consumer = new Thread(data::waitForData);
        Thread producer = new Thread(() -> {
            try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
            data.produceData();
        });

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

✅ Output is the same, but…

🧠 Why Use Condition Over wait()/notify()?

Featurewait()/notify()Condition (with Lock)
Number of condition queues1 per monitor✅ Multiple possible
Targeted signaling❌ No (notify())✅ Yes (condition.signal())
Locking flexibilityOnly intrinsic locks (synchronized)✅ Any Lock (like ReentrantLock)
Reentrant support✅ Yes✅ Yes
Fairness, timeout controlLimited✅ Full support

🧩 Summary

  • Use wait()/notify() for simple coordination inside synchronized blocks
  • Use Condition if you need:
    • Multiple wait conditions
    • Better signaling control
    • Integration with ReentrantLock, tryLock, or advanced behavior
This entry was posted in Без рубрики. Bookmark the permalink.