Back to Advanced: Disruptor, Actor, False Sharing
Q1Question 1 of 5

Two threads print 1,A,2,B,3,C alternately?

Key Concepts
  • Use a shared boolean flag protected by synchronized plus wait() and notify() to alternate two threads.
  • The while(!myTurn) wait() pattern handles spurious wakeups and ensures the correct thread proceeds.
  • Thread-1 prints a number, sets the flag to the other thread's turn, calls notify(), then waits.
  • Thread-2 prints a letter, flips the flag back, calls notify(), then waits.
  • The pattern generalizes to N threads by replacing the boolean with an int and using notifyAll().
  • Alternative implementations use Lock plus Condition, Semaphore pairs, or LockSupport.park/unpark.
  • The key insight is that wait() releases the monitor so the other thread can enter the synchronized block.

Definition

Two threads alternate output (1,A,2,B,3,C) by sharing a boolean flag protected with synchronized, wait(), and notify(), where each thread flips the flag after printing and wakes the other.
  • Shared flag – a boolean numberTurn indicates whose turn it is. The flag is protected by a shared lock object.
  • Synchronized block – both threads enter synchronized(lock) to check and modify the flag safely.
  • while(!myTurn) wait() – the canonical pattern: check the condition in a while loop (not if) to handle spurious wakeups, and call wait() to release the monitor and park the thread.
  • Print, flip, notify – the active thread prints its value, sets the flag to the other thread's turn, and calls notify() to wake the waiting thread.
  • wait() releases the lock – this is critical. When Thread-1 calls lock.wait(), it releases the monitor on lock, allowing Thread-2 to enter the synchronized block.
  • notify() wakes one – notify() wakes exactly one thread waiting on this monitor. Since only one other thread is waiting, it always wakes the right one.

Think of two tellers at National Bank of India sharing a single microphone at a customer service counter. Rajesh announces a token number, then passes the microphone to Priya and sits down. Priya announces the counter letter, passes the microphone back to Rajesh, and sits down. They repeat: "Token 1" (Rajesh), "Counter A" (Priya), "Token 2" (Rajesh), "Counter B" (Priya). The microphone is the lock, sitting down is wait(), and passing the microphone is notify().

Understanding the Concepts

What is the alternate printing problem?

The alternate printing problem asks you to use two threads to produce interleaved output. Thread-1 prints numbers (1, 2, 3, ...) and Thread-2 prints letters (A, B, C, ...). The output must be strictly alternating: 1, A, 2, B, 3, C. This is a classic interview question testing understanding of inter-thread communication using wait() and notify().

How do you solve it with wait/notify?

The solution uses a shared boolean flag to control turns. Both threads synchronize on the same lock object, check the flag in a while loop, and call wait() when it is not their turn. After printing, the active thread flips the flag and calls notify() to wake the other thread.

JAVA
package com.bank.concurrency;

public class AlternatePrinter {
    private boolean numberTurn = true;
    private final Object lock = new Object();

    public void printNumbers(int count) {
        for (int i = 1; i <= count; i++) {
            synchronized (lock) {
                while (!numberTurn) {
                    try { lock.wait(); } catch (InterruptedException e) { return; }
                }
                System.out.print(i + ",");
                numberTurn = false;
                lock.notify();
            }
        }
    }

    public void printLetters(int count) {
        for (int i = 0; i < count; i++) {
            synchronized (lock) {
                while (numberTurn) {
                    try { lock.wait(); } catch (InterruptedException e) { return; }
                }
                System.out.print((char) ('A' + i) + ",");
                numberTurn = true;
                lock.notify();
            }
        }
    }

    public static void main(String[] args) {
        AlternatePrinter printer = new AlternatePrinter();
        Thread t1 = new Thread(() -> printer.printNumbers(5), "NumberThread");
        Thread t2 = new Thread(() -> printer.printLetters(5), "LetterThread");
        t1.start();
        t2.start();
    }
}
TEXT
1,A,2,B,3,C,4,D,5,E,

Why must you use while instead of if for the wait condition?

A thread can wake up without being notified, which is called a spurious wakeup. The JVM specification allows this behavior. If you use if (!numberTurn) wait(), the thread may resume even though the flag has not changed, print out of order, and break the alternation. The while loop re-checks the condition after every wakeup, ensuring the thread only proceeds when it is genuinely its turn.

JAVA
// WRONG: if allows spurious wakeup to break alternation
synchronized (lock) {
    if (!numberTurn) {
        lock.wait();  // spurious wakeup: proceeds even if flag unchanged!
    }
    System.out.print(i);  // may print out of order
}

// CORRECT: while re-checks after every wakeup
synchronized (lock) {
    while (!numberTurn) {
        lock.wait();  // spurious wakeup: loops back and checks again
    }
    System.out.print(i);  // only proceeds when it is genuinely our turn
}

How do you solve it with Lock and Condition?

ReentrantLock with two Condition objects provides a cleaner separation. Each thread waits on its own Condition and signals the other thread's Condition.

JAVA
package com.bank.concurrency;

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

public class AlternatePrinterLock {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition numberCondition = lock.newCondition();
    private final Condition letterCondition = lock.newCondition();
    private boolean numberTurn = true;

    public void printNumbers(int count) {
        for (int i = 1; i <= count; i++) {
            lock.lock();
            try {
                while (!numberTurn) numberCondition.await();
                System.out.print(i + ",");
                numberTurn = false;
                letterCondition.signal();
            } catch (InterruptedException e) { return;
            } finally { lock.unlock(); }
        }
    }

    public void printLetters(int count) {
        for (int i = 0; i < count; i++) {
            lock.lock();
            try {
                while (numberTurn) letterCondition.await();
                System.out.print((char) ('A' + i) + ",");
                numberTurn = true;
                numberCondition.signal();
            } catch (InterruptedException e) { return;
            } finally { lock.unlock(); }
        }
    }
}

How do you generalize to N threads?

Replace the boolean flag with an int that cycles from 0 to N-1. Each thread checks if the current turn matches its index. Use notifyAll() instead of notify() because you need to wake all threads so the correct one can proceed.

JAVA
package com.bank.concurrency;

public class NThreadAlternate {
    private int currentTurn = 0;
    private final int totalThreads;
    private final Object lock = new Object();

    public NThreadAlternate(int totalThreads) {
        this.totalThreads = totalThreads;
    }

    public void print(int threadId, String[] values) {
        for (String value : values) {
            synchronized (lock) {
                while (currentTurn != threadId) {
                    try { lock.wait(); } catch (InterruptedException e) { return; }
                }
                System.out.print(value + ",");
                currentTurn = (currentTurn + 1) % totalThreads;
                lock.notifyAll();
            }
        }
    }
}

What are the common mistakes?

The most common mistake is calling notify() before wait(). If Thread-1 calls notify() but Thread-2 has not yet called wait(), the notification is lost and Thread-2 waits forever. The shared flag prevents this: Thread-2 checks the flag first and only calls wait() if it is not its turn.

JAVA
// WRONG: notify before wait is lost
Object lock = new Object();
// Thread-1 runs first:
synchronized (lock) { lock.notify(); }  // nobody waiting, signal LOST!
// Thread-2 runs second:
synchronized (lock) { lock.wait(); }    // waits FOREVER, nobody to notify

// CORRECT: flag prevents lost notification
boolean ready = false;
// Thread-1:
synchronized (lock) { ready = true; lock.notify(); }
// Thread-2:
synchronized (lock) { while (!ready) lock.wait(); }  // if ready is true, skips wait

How is this applied in banking systems?

In a banking audit system, compliance requires that debit and credit log entries alternate strictly for reconciliation. A debit-logger thread and a credit-logger thread use this pattern to ensure the audit trail is interleaved correctly without any two consecutive debits or credits.

What are common interview follow-up questions?

  • Can you do this without wait/notify? Yes, using two Semaphores with initial permits of 1 and 0, or using LockSupport.park/unpark with a shared volatile flag.
  • What happens if one thread is interrupted? The InterruptedException breaks the thread out of wait(), and the finally block should signal the other thread to prevent it from waiting forever.
  • Can you use a BlockingQueue? Yes, two SynchronousQueues can act as handoff signals between the threads.

Visual Overview

DIAGRAM
  Alternate Printing: Two Threads with wait/notify
  ═════════════════════════════════════════════════

  NumberThread                    LetterThread
  ───────────                     ────────────
  sync(lock)                      sync(lock)
  numberTurn=true → print "1"     numberTurn=true → while(!turn) wait()
  set numberTurn=false                  │
  notify() ─────────────────────→ wakes up, checks flag
  wait() ←─────────────────────── numberTurn=false → print "A"
       │                          set numberTurn=true
  wakes up, checks flag ←──────── notify()
  numberTurn=true → print "2"     wait()
  set numberTurn=false                  │
  notify() ─────────────────────→ wakes up → print "B"
  ...                             ...

  Output: 1,A,2,B,3,C,4,D,5,E

Key Takeaway

The alternate printing problem is solved by sharing a boolean flag between two threads, protected by synchronized plus wait() and notify(). Each thread checks the flag in a while loop, prints when it is its turn, flips the flag, and calls notify() to wake the other thread before calling wait() itself. The while loop handles spurious wakeups by re-checking the condition after every resumption.

The key insight is that wait() releases the monitor lock, which is what allows the other thread to enter the synchronized block and proceed. Without this release, the second thread would be blocked on the synchronized boundary forever. The pattern generalizes to N threads by replacing the boolean with an int that cycles through thread indices and using notifyAll() instead of notify().

At National Bank of India, this pattern was used in an audit logging system where debit and credit entries had to alternate strictly for regulatory compliance. The debit logger and credit logger threads used a shared flag with wait/notify to guarantee interleaved output without any two consecutive entries of the same type.

Remember one sentence: alternate two threads by sharing a boolean flag with synchronized, wait(), and notify(), where each thread prints, flips the flag, notifies the other, and waits.

---

Share
Use ← → arrow keys to navigate