1. Problem Description
We want:
- Thread 1 (OddThread) → prints odd numbers: 1, 3, 5, 7, …
- Thread 2 (EvenThread) → prints even numbers: 2, 4, 6, 8, …
Both should print numbers in sequence, i.e.,1 2 3 4 5 6 7 8 ... (not random interleaving).
To achieve this, we’ll make the two threads communicate and coordinate using wait() and notify() on a shared object lock.
2. Core Idea
- Both threads share a common lock object and a shared counter variable.
- Each thread checks whether it’s its turn (odd or even).
- If it’s not its turn, it calls
wait(). - After printing, it increments the counter and calls
notify()to wake the other thread.
3. Step-by-Step Logic
- Initialize a shared variable
number = 1. - Define a limit, say
MAX = 10. - Create one thread for odd numbers and one for even numbers.
- Use a common lock object:
- When the odd thread runs:
- It prints only if
number % 2 != 0. - After printing, it increments and notifies the even thread.
- It prints only if
- When the even thread runs:
- It prints only if
number % 2 == 0. - After printing, it increments and notifies the odd thread.
- It prints only if
- When the odd thread runs:
This ensures alternating execution.
4. Complete Java Program
public class EvenOddThreadSync {
private int number = 1; // Shared counter
private final int MAX = 10; // Upper limit
private final Object lock = new Object(); // Common lock object
public static void main(String[] args) {
EvenOddThreadSync printer = new EvenOddThreadSync();
Thread oddThread = new Thread(() -> printer.printOdd());
Thread evenThread = new Thread(() -> printer.printEven());
oddThread.start();
evenThread.start();
}
// Method for odd numbers
public void printOdd() {
synchronized (lock) {
while (number <= MAX) {
while (number % 2 == 0) { // Wait if it's even's turn
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (number <= MAX) {
System.out.println("Odd Thread: " + number);
number++;
lock.notify(); // Wake up even thread
}
}
}
}
// Method for even numbers
public void printEven() {
synchronized (lock) {
while (number <= MAX) {
while (number % 2 != 0) { // Wait if it's odd's turn
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
if (number <= MAX) {
System.out.println("Even Thread: " + number);
number++;
lock.notify(); // Wake up odd thread
}
}
}
}
}
5. Sample Output
Odd Thread: 1 Even Thread: 2 Odd Thread: 3 Even Thread: 4 Odd Thread: 5 Even Thread: 6 Odd Thread: 7 Even Thread: 8 Odd Thread: 9 Even Thread: 10
6. How It Works Internally
- Both threads start and compete for the lock.
- Initially,
number = 1, soprintOdd()executes. - The odd thread prints 1, increments the counter to 2, and calls
notify(). - The even thread wakes up, prints 2, increments the counter to 3, and calls
notify(). - This alternating pattern continues until the limit is reached.
The wait() and notify() methods ensure that:
- Only one thread prints at a time.
- Threads alternate execution in perfect sync.
7. Alternative: Using Lock and Condition (Modern Approach)
A cleaner way using Java’s ReentrantLock and Condition:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class EvenOddLockExample {
private int number = 1;
private final int MAX = 10;
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public static void main(String[] args) {
EvenOddLockExample printer = new EvenOddLockExample();
Thread oddThread = new Thread(printer::printOdd);
Thread evenThread = new Thread(printer::printEven);
oddThread.start();
evenThread.start();
}
public void printOdd() {
while (number <= MAX) {
lock.lock();
try {
while (number % 2 == 0) {
condition.await();
}
if (number <= MAX) {
System.out.println("Odd Thread: " + number);
number++;
condition.signalAll();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
public void printEven() {
while (number <= MAX) {
lock.lock();
try {
while (number % 2 != 0) {
condition.await();
}
if (number <= MAX) {
System.out.println("Even Thread: " + number);
number++;
condition.signalAll();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
}
This approach avoids intrinsic locks (synchronized) and gives finer control over thread signaling.
8. Key Takeaways
| Concept | Explanation |
|---|---|
| Shared object | Ensures both threads coordinate using a single lock |
wait() | Causes the thread to pause and release the lock |
notify() | Wakes up one waiting thread on the same lock |
| Synchronization | Ensures visibility and mutual exclusion |
| ReentrantLock | Modern alternative to synchronized with explicit control |
| Condition | Provides finer signaling control (similar to wait/notify) |
