Java Multithreading Puzzle: Concurrent Printers


In this article, we will explore an intriguing Java multithreading puzzle. This puzzle will not only test your knowledge of Java multithreading but also improve your problem-solving skills. We will walk you through the problem statement, solution, and provide detailed explanations. Let’s dive into the world of concurrent programming with Java!


Problem Statement


You have three threads: ThreadA, ThreadB, and ThreadC. Each thread prints a sequence of numbers in a loop. Implement these threads such that the numbers are printed in the following sequence:

java multithreading in action - three threads simultaneously printing

The goal is to ensure that the numbers are printed in the correct order across the threads using Java multithreading concepts.


Solution


Step-by-Step Explanation


  1. Class Structure:
    • ConcurrentPrinters: Main class that sets up the threads and starts execution.
    • Printer: Inner class that implements Runnable and defines the printing behavior for each thread.
  2. Variables:
    • MAX_COUNT: Maximum number of times each thread should print.
    • currentNumber: Tracks the current number to be printed.
    • lock: Object used for synchronization.

Java Code Implementation


import java.util.concurrent.*;

public class ThreadConcurrentPrinters {
    private static final int MAX_COUNT = 20;
    private static volatile int currentNumber = 1;
    private static final Object lock = new Object();

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        executor.submit(new Printer("ThreadA", 1));
        executor.submit(new Printer("ThreadB", 2));
        executor.submit(new Printer("ThreadC", 3));

        executor.shutdown();
    }

    static class Printer implements Runnable {
        private String name;
        private int startNumber;

        public Printer(String name, int startNumber) {
            this.name = name;
            this.startNumber = startNumber;
        }

        @Override
        public void run() {
            while (true) {
                synchronized (lock) {
                    while (currentNumber % 3 != startNumber - 1) { // startNumber - 1 for 0-based index
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            return;
                        }
                    }

                    if (currentNumber > MAX_COUNT) {
                        return;
                    }

                    System.out.println(name + ": " + currentNumber);
                    currentNumber++;
                    lock.notifyAll();
                }
            }
        }
    }
}
    

Example Output


Given the constraints, here’s an example of the output you would expect:

ThreadA: 1
ThreadB: 2
ThreadC: 3
ThreadA: 4
ThreadB: 5
ThreadC: 6
ThreadA: 7
ThreadB: 8
ThreadC: 9
ThreadA: 10
ThreadB: 11
ThreadC: 12
ThreadA: 13
ThreadB: 14
ThreadC: 15
ThreadA: 16
ThreadB: 17
ThreadC: 18
ThreadA: 19
ThreadB: 20
    

Understanding the Output


This example demonstrates how each thread prints its respective numbers in sequence:

ThreadA: 1, 4, 7, 10, ...
ThreadB: 2, 5, 8, 11, ...
ThreadC: 3, 6, 9, 12, ...
    

Explanation


Thread Setup:

ConcurrentPrinters creates a thread pool with three threads (ThreadA, ThreadB, ThreadC). Each thread starts with a different initial number (1, 2, 3).

Printing Logic:

Each thread (Printer) enters a loop and waits until it’s its turn (currentNumber % 3). The threads print in a sequential order (ThreadA, ThreadB, ThreadC). The threads use synchronized blocks with lock object to ensure only one thread can access currentNumber at a time. After printing, the thread increments currentNumber and notifies all threads waiting on lock.




Checkout ThreadConcurrentPrinters.java on GitHub



Conclusion

This puzzle demonstrates an understanding of Java multithreading concepts, such as synchronization, mutual exclusion, and thread communication using wait-notify mechanisms. It ensures that the threads print numbers in a specific sequence while avoiding race conditions and ensuring thread safety.



Read Next