1195. Fizz Buzz Multithreaded (Leetcode Concurrency Section)
Fizz Buzz Multithreaded - Video Solution
Watch 4 video solutions for Fizz Buzz Multithreaded, a medium level problem involving Concurrency. This walkthrough by EverydayLeetcode has 4,042 views views. Want to try solving it yourself? Practice on FleetCode or read the detailed text solution.
Problem Statement
You have the four functions:
printFizzthat prints the word"fizz"to the console,printBuzzthat prints the word"buzz"to the console,printFizzBuzzthat prints the word"fizzbuzz"to the console, andprintNumberthat prints a given integer to the console.
You are given an instance of the class FizzBuzz that has four functions: fizz, buzz, fizzbuzz and number. The same instance of FizzBuzz will be passed to four different threads:
- Thread A: calls
fizz()that should output the word"fizz". - Thread B: calls
buzz()that should output the word"buzz". - Thread C: calls
fizzbuzz()that should output the word"fizzbuzz". - Thread D: calls
number()that should only output the integers.
Modify the given class to output the series [1, 2, "fizz", 4, "buzz", ...] where the ith token (1-indexed) of the series is:
"fizzbuzz"ifiis divisible by3and5,"fizz"ifiis divisible by3and not5,"buzz"ifiis divisible by5and not3, oriifiis not divisible by3or5.
Implement the FizzBuzz class:
FizzBuzz(int n)Initializes the object with the numbernthat represents the length of the sequence that should be printed.void fizz(printFizz)CallsprintFizzto output"fizz".void buzz(printBuzz)CallsprintBuzzto output"buzz".void fizzbuzz(printFizzBuzz)CallsprintFizzBuzzto output"fizzbuzz".void number(printNumber)Callsprintnumberto output the numbers.
Example 1:
Input: n = 15 Output: [1,2,"fizz",4,"buzz","fizz",7,8,"fizz","buzz",11,"fizz",13,14,"fizzbuzz"]
Example 2:
Input: n = 5 Output: [1,2,"fizz",4,"buzz"]
Constraints:
1 <= n <= 50
Approach Overview
Problem Overview: Four threads must cooperatively print numbers from 1 to n. One thread prints fizz for multiples of 3, another prints buzz for multiples of 5, another prints fizzbuzz for multiples of both, and the last prints the number itself. The challenge is coordinating these threads so output appears in the correct order without race conditions.
This is a classic concurrency control problem. Multiple workers want to act on the same sequence of integers. Without synchronization, threads can interleave unpredictably and produce incorrect output. The goal is to enforce ordering while keeping the logic simple and efficient.
Approach 1: Synchronize with Locks (Time: O(n), Space: O(1))
A shared counter tracks the current number from 1 to n. Each thread repeatedly acquires a mutual exclusion lock, checks whether the current number satisfies its condition, prints if appropriate, and increments the counter. If the condition does not match, the thread releases the lock so another worker can check. The lock guarantees that only one thread evaluates and updates the shared state at a time, preventing race conditions. This pattern is common in multithreading problems where threads coordinate through a shared variable.
The key idea is that the condition check and counter increment must be atomic. The lock ensures ordering and correctness, though threads may repeatedly wake and check the condition until it matches. The algorithm processes each number exactly once, so total work is O(n) with constant auxiliary memory.
Approach 2: Semaphore for Synchronization (Time: O(n), Space: O(1))
A more structured solution uses semaphores to explicitly control which thread runs next. A main semaphore handles the number-checking thread. Depending on the value of the current number, it releases one of three semaphores: fizz, buzz, or fizzbuzz. The corresponding worker prints its output and signals back so the next number can be processed.
This design avoids busy checking because each thread sleeps until its semaphore is released. The number thread determines which worker should run, ensuring the correct order and condition handling. Each integer triggers exactly one worker action, so time complexity remains O(n) and memory usage stays constant.
Semaphore-based coordination is common in interview problems involving ordered thread execution. It clearly separates responsibilities between threads and avoids repeated condition polling.
Recommended for interviews: The semaphore approach is usually preferred. It demonstrates clear reasoning about thread coordination and avoids inefficient lock polling. The lock-based method still shows you understand mutual exclusion and shared state protection, but semaphores communicate intent more directly in concurrency design.
Complexity Analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Synchronize with Locks | O(n) | O(1) | When coordinating threads using a shared counter with mutual exclusion |
| Semaphore for Synchronization | O(n) | O(1) | Preferred when explicit thread signaling is needed to control execution order |