Knowledge Guide
HomeConcurrencyConcurrency Problems

Problem 8 ZeroEvenOdd Multithreading Problem

Overview

In the ZeroEvenOdd problem, we aim to print a sequence in the format "01020304...". The challenge is that we have three different threads responsible for printing parts of this sequence: one prints zeros, one prints even numbers, and one prints odd numbers. Proper synchronization between these threads is crucial to ensure the sequence is printed correctly and without race conditions. Our task is to structure and synchronize the threads to print this sequence up to 2n collaboratively.

Step-by-step Algorithm

  1. Initialize

    • Start with the first number (0).
    • Set up synchronization tools (locks, conditions, etc.).
  2. Thread A (Zero Printer)

    • Responsible for printing zeros.
  3. Thread B (Even Number Printer)

    • Prints even numbers in sequence.
  4. Thread C (Odd Number Printer)

    • Prints odd numbers in sequence.
  5. Synchronization

    • Ensure Thread A runs first, followed by either Thread B or Thread C based on whether the next number is even or odd.
  6. Wrap-up

    • Continue until the sequence length reaches 2n.

Algorithm Walkthrough

Assume n = 2. Our sequence should be "0102".

  1. Initialization:
    • Set n = 2.
    • Initialize counter = 1.
  2. Zero Printing (zero() function):
    • Print "0" because it's the starting character of the sequence.
  3. Odd Printing (odd() function):
    • Since counter is 1 (odd), this thread prints "1".
    • Increment counter to 2.
    • Signal to the Zero thread by unlocking zero_lock.
  4. Zero Printing (zero() function):
    • Print "0" following the odd number.
  5. Even Printing (even() function):
    • With counter at 2 (even), this thread prints "2".
    • Signal the end of the sequence as counter now equals n.

Synchronization Explained

Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Solution {

  // Declare instance variables that are essential for our solution
  private int n; // The number up to which we need to print
  private int counter = 1; // Counter to keep track of the current number being printed

  // Use ReentrantLock for synchronization as it's more flexible than synchronized methods or blocks
  private final Lock lock = new ReentrantLock();

  // Conditions are used to coordinate threads. Here, we declare three conditions:
  // one for zeros, one for even numbers, and one for odd numbers
  private final Condition zeroCondition = lock.newCondition();
  private final Condition evenCondition = lock.newCondition();
  private final Condition oddCondition = lock.newCondition();

  // Flags to keep track of whose turn it is to print
  private boolean zeroTurn = true; // We start with zero
  private boolean oddTurn = true; // We start with odd after zero, before even

  // Constructor: Initializes the object and sets the value of n
  public Solution(int n) {
    this.n = n;
  }

  // Method for thread A to print zeros
  public void zero() throws InterruptedException {
    for (int i = 0; i < n; i++) {
      lock.lock(); // Acquire the lock before checking conditions and printing
      try {
        // Keep the thread in a waiting state while it's not zero's turn to print
        while (!zeroTurn) {
          zeroCondition.await();
        }

        printNumber(0); // Once out of the wait state, print zero

        zeroTurn = false; // Switch the flag since zero has been printed

        // Depending on whether the next number is odd or even, signal the appropriate thread to take over
        if (oddTurn) {
          oddCondition.signal();
        } else {
          evenCondition.signal();
        }
      } finally {
        lock.unlock(); // Always release the lock in the finally block to ensure it's released under all circumstances
      }
    }
  }

  // Method for thread B to print even numbers
  public void even() throws InterruptedException {
    for (int i = 2; i <= n; i += 2) {
      lock.lock();
      try {
        // The even thread waits if it's zero's turn to print or if the current number is odd
        while (zeroTurn || oddTurn) {
          evenCondition.await();
        }

        printNumber(i); // Print the even number

        zeroTurn = true; // Switch the turn back to zero
        oddTurn = true; // After an even, the next number will be odd

        zeroCondition.signal(); // Signal the zero thread to take over
      } finally {
        lock.unlock();
      }
    }
  }

  // Method for thread C to print odd numbers
  public void odd() throws InterruptedException {
    for (int i = 1; i <= n; i += 2) {
      lock.lock();
      try {
        // The odd thread waits if it's zero's turn or if the current number is even
        while (zeroTurn || !oddTurn) {
          oddCondition.await();
        }

        printNumber(i); // Print the odd number

        zeroTurn = true; // Switch the turn back to zero
        oddTurn = false; // After an odd, the next number will be even

        zeroCondition.signal(); // Signal the zero thread to take over
      } finally {
        lock.unlock();
      }
    }
  }

  // Utility method to actually output the number to the console
  public void printNumber(int num) {
    System.out.print(num);
  }

  // Main method to demonstrate the solution
  public static void main(String[] args) {
    // Create an instance of the ZeroEvenOdd class with a value of 5
    Solution zeo = new Solution(5);

    // Create three threads: one for zero, one for even numbers, and one for odd numbers
    Thread t1 = new Thread(() -> {
      try {
        zeo.zero();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });

    Thread t2 = new Thread(() -> {
      try {
        zeo.even();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });

    Thread t3 = new Thread(() -> {
      try {
        zeo.odd();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    });

    // Start all the threads
    t1.start();
    t2.start();
    t3.start();
  }
}
🤖 Don't fully get this? Learn it with Claude

Stuck on Problem 8 ZeroEvenOdd Multithreading Problem? Open Claude, copy a block below, and it'll teach you this exact concept — visually and interactively.

🪜 Hint ladder (no spoilers)

Progressively stronger hints — you still solve it.

I'm working on the problem **Problem 8 ZeroEvenOdd Multithreading Problem** (Concurrency). Give me a HINT LADDER: start with the tiniest nudge, then wait. Only reveal the next, stronger hint when I ask. Do NOT show the full solution unless I type 'show solution'. Keep me doing the thinking. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🎨 Explain the approach visually

See the technique, not just code.

Explain the optimal approach to **Problem 8 ZeroEvenOdd Multithreading Problem** with a VISUAL walkthrough: trace it on a small concrete example using ASCII art / a step-by-step diagram, narrate what changes each step, then give time & space complexity with a one-line derivation. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔍 Review my solution

Catch bugs, edge cases, sub-optimality.

I'll paste my solution to **Problem 8 ZeroEvenOdd Multithreading Problem**. Review it for correctness, missed edge cases, and time/space complexity, then coach me toward the optimal — don't just rewrite it. Ask me to paste my code now. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.
🔁 Drill the pattern

Lock in recognition with look-alikes.

Give me 2 problems that use the SAME underlying pattern as **Problem 8 ZeroEvenOdd Multithreading Problem**. For each, let me attempt first, then review my answer and name the trigger signal that reveals the pattern. If you're unsure or a claim isn't standard, say so and reason from first principles instead of guessing.

📝 My notes