FizzBuzz with Four Threads
Problem
Print the numbers 1 to n, one per line, but print Fizz for multiples of 3, Buzz for multiples of 5 and FizzBuzz for multiples of both. Use four threads: one prints plain numbers, one prints Fizz, one prints Buzz and one prints FizzBuzz. Each thread waits for its turn, so the output order is always correct.
Constraints
- 1 <= n <= 1000
Examples
Input
5
Output
1 2 Fizz 4 Buzz
Solutions
All four threads look at the same number. Only the owner of that number may print it. The others wait. After printing, notifyAll wakes everyone to check whose turn it is. When the number is above n every thread leaves.
/*
* Problem : FizzBuzz.
* Input : n.
* Output : n lines.
* Approach: Four threads taking turns.
* Time : O(n)
* Space : O(n)
* Java : 8 or newer.
*/
import java.util.Scanner;
public class Main {
// Which thread owns the number: 0 plain number, 1 Fizz, 2 Buzz, 3 FizzBuzz.
private static int ownerOf(int i) {
if (i % 15 == 0) {
return 3;
}
if (i % 3 == 0) {
return 1;
}
return i % 5 == 0 ? 2 : 0;
}
// This method solves the task. It receives the input and returns the answer.
static String[] solve(long n) {
// Step 1: "next" is the number that must be printed now. All threads share it and the output list under one lock.
final int limit = (int) n;
final Object lock = new Object();
final int[] next = {1};
final java.util.List<String> output = new java.util.ArrayList<String>();
Thread[] threads = new Thread[4];
for (int t = 0; t < 4; t++) {
final int me = t;
threads[t] = new Thread(() -> {
synchronized (lock) {
while (true) {
// Step 2: Wait while it is not my turn. wait() gives the lock away until someone calls notifyAll().
while (next[0] <= limit && ownerOf(next[0]) != me) {
try {
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
if (next[0] > limit) {
lock.notifyAll();
return;
}
// Step 3: It is my turn. Print, move to the next number and wake the other threads.
int value = next[0];
output.add(me == 0 ? String.valueOf(value) : me == 1 ? "Fizz" : me == 2 ? "Buzz" : "FizzBuzz");
next[0]++;
lock.notifyAll();
}
}
});
threads[t].start();
}
for (Thread thread : threads) {
try {
thread.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
return output.toArray(new String[0]);
}
public static void main(String[] args) {
long n = new Scanner(System.in).nextLong();
System.out.println(String.join("\n", solve(n)));
}
}
Hints (2)
- Why use a while loop around wait()?
- Why do we call notifyAll and not notify?