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

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.


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:

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:

Now the values alternate correctly between 0 and 1.