Multithreaded Producer-Consumer with Synchronized in Java

Problem Statement

We need two threads to operate on a shared variable. One thread increments the variable by 1, the other decrements it by 1, and they must alternate. The initial value is 0. In other words, two threads perform alternating increment and decrement operations on a common resource. Let's get started.

First, define the resource class and its methods.

Resource Class

// Resource class
class Resource {
    private int number = 0;

    public synchronized void up() throws InterruptedException {
        // 1. Check condition
        if (number != 0) {
            this.wait();
        }
        // 2. Perform work
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }

    public synchronized void down() throws InterruptedException {
        if (number == 0) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }
}

Two Threads

public class ThreadWaitNotifyDemo {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.up();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.down();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
    }
}

Result

Two threads result

Here we use lambda expressions to create threads via anonymous inner classes. Thread A calls the up method, thread B calls down. For example, if thread A checks that the number is 0, it increments. At the same time, if thread B checks and finds the number is not 0 (since A incremented), it waits. After A finishes incrementing, it calls notifyAll() to wake up other threads, allowing the decrement to proceed. The loop runs 10 times to ensure 10 alternating operations.

A Common Misconception: Are wait() and notify() Methods of Thread?

No, they are not. They belong to the Object class. Additionally, wait() and notify() must always be used within a synchronized block or method.

API screenshot 1

API screenshot 2

But is that all?

Changing Requirements

Suddenly, the project manager says: "I don't want two threadds anymore; I need four threads. Two threads will increment, and two will decrement. They must still alternate strictly between 0 and 1."

You think it's simple: just add two more threads.

Code (Resource unchanged):

public class ThreadWaitNotifyDemo {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.up();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.down();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.up();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.down();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

Result:

Four threads incorrect result

Oops, we see values like 3 appearing. What happened?

Analysis: Threads A and C perform incerments; B and D perform decrements. When the number is 0, B and D are waiting. A and C are both blocked by the synchronized lock, so only one can enter up(). Suppose A enters, increments to 1, then calls notifyAll(). At that point, C might also be waiting at if (number != 0) { this.wait(); }. After being notified, C continues without rechecking the condition. Since A just set number to 1, C proceeds to increment again, making it 2. Similarly, B and D can wake up and decrement without rechecking, leading to values other than 0 and 1.

Solution

The problem is that after being notified, the thread does not re-evaluate the condition. The fix is to use a while loop instead of an if statement, so that the condition is rechecked after waking up.

Modified Resource class:

class Resource {
    private int number = 0;

    public synchronized void up() throws InterruptedException {
        // 1. Check condition in a loop
        while (number != 0) {
            this.wait();
        }
        // 2. Perform work
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }

    public synchronized void down() throws InterruptedException {
        while (number == 0) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }
}

The four-thread code remains unchanged. Result:

Four threads correct result

Now the values alternate correctly between 0 and 1.

Tags: java multithreading Synchronized producer-consumer Concurrency

Posted on Tue, 26 May 2026 19:25:12 +0000 by ron8000