1195. Fizz Buzz Multithreaded
Problem Description
This is a multithreading problem where you need to implement a FizzBuzz
class that coordinates four different threads to print the classic FizzBuzz sequence.
The problem provides you with four printing functions:
printFizz()
- prints "fizz"printBuzz()
- prints "buzz"printFizzBuzz()
- prints "fizzbuzz"printNumber(int x)
- prints the integer x
You need to implement a FizzBuzz
class with four methods that will be called by four separate threads:
- Thread A calls
fizz()
- responsible for printing "fizz" when appropriate - Thread B calls
buzz()
- responsible for printing "buzz" when appropriate - Thread C calls
fizzbuzz()
- responsible for printing "fizzbuzz" when appropriate - Thread D calls
number()
- responsible for printing numbers when appropriate
The class should coordinate these threads to produce the correct FizzBuzz sequence from 1 to n:
- Print "fizzbuzz" if the number is divisible by both 3 and 5
- Print "fizz" if the number is divisible by 3 but not 5
- Print "buzz" if the number is divisible by 5 but not 3
- Print the number itself if it's not divisible by either 3 or 5
For example, if n = 15
, the output should be:
[1, 2, "fizz", 4, "buzz", "fizz", 7, 8, "fizz", "buzz", 11, "fizz", 13, 14, "fizzbuzz"]
The key challenge is synchronizing the four threads so that:
- Each thread only prints when it's their turn
- The output appears in the correct sequential order from 1 to n
- No race conditions or deadlocks occur
The solution uses semaphores to coordinate thread execution. Each type of output (fizz, buzz, fizzbuzz, number) has its own semaphore to control when that thread can execute. The number
thread acts as the coordinator, determining which thread should run next based on the current number's divisibility rules.
Intuition
The core challenge here is that we have four threads that need to take turns printing in a specific order. Think of it like a relay race where only one runner can run at a time, and we need to pass the baton to the correct next runner based on the current position.
The key insight is that we need a coordination mechanism where:
- Only one thread can execute at any given moment
- We need to signal which specific thread should run next
- After a thread finishes its task, it must signal the coordinator to continue
Semaphores are perfect for this because they can block threads until signaled. We can think of each semaphore as a gate that's initially closed (initialized to 0), and threads wait at their gate until it opens.
The natural approach is to have the number()
thread act as the coordinator since it needs to check every number from 1 to n anyway. For each number i
, it determines which output is needed:
- If
i % 15 == 0
→ signal the fizzbuzz thread - If
i % 3 == 0
(but not 15) → signal the fizz thread - If
i % 5 == 0
(but not 15) → signal the buzz thread - Otherwise → print the number itself
We use four semaphores:
nSema
(initialized to 1) - controls the number thread, starts with 1 so it runs firstfSema
(initialized to 0) - controls the fizz threadbSema
(initialized to 0) - controls the buzz threadfbSema
(initialized to 0) - controls the fizzbuzz thread
The workflow becomes:
- The number thread acquires
nSema
(consuming the permit) - Based on the current number, it either:
- Releases a permit to one of the other semaphores (delegating the print job)
- Prints the number itself and releases
nSema
again
- The signaled thread acquires its semaphore, prints, then releases
nSema
to return control - This continues until all numbers from 1 to n are processed
Each specialized thread (fizz, buzz, fizzbuzz) cleverly iterates only through numbers it might need to handle (incrementing by 3, 5, or 15 respectively), making the solution more efficient than checking every single number.
Solution Approach
The solution uses semaphores as the synchronization mechanism to coordinate the four threads. Let's walk through the implementation step by step:
Data Structures
- Four Semaphores are used for thread coordination:
nSema = new Semaphore(1)
- Controls the number thread (starts with 1 permit)fSema = new Semaphore(0)
- Controls the fizz thread (starts blocked)bSema = new Semaphore(0)
- Controls the buzz thread (starts blocked)fbSema = new Semaphore(0)
- Controls the fizzbuzz thread (starts blocked)
The Coordinator Thread - number()
This is the main orchestrator that runs through all numbers from 1 to n:
for (int i = 1; i <= n; i++) { nSema.acquire(); // Wait for permission to process number i if (i % 3 == 0 && i % 5 == 0) { fbSema.release(); // Signal fizzbuzz thread } else if (i % 3 == 0) { fSema.release(); // Signal fizz thread } else if (i % 5 == 0) { bSema.release(); // Signal buzz thread } else { printNumber.accept(i); // Print the number directly nSema.release(); // Give control back to itself } }
The Worker Threads
Fizz Thread:
for (int i = 3; i <= n; i = i + 3) { // Only check multiples of 3 if (i % 5 != 0) { // Skip multiples of 15 (handled by fizzbuzz) fSema.acquire(); // Wait for signal printFizz.run(); // Print "fizz" nSema.release(); // Return control to number thread } }
Buzz Thread:
for (int i = 5; i <= n; i = i + 5) { // Only check multiples of 5 if (i % 3 != 0) { // Skip multiples of 15 (handled by fizzbuzz) bSema.acquire(); // Wait for signal printBuzz.run(); // Print "buzz" nSema.release(); // Return control to number thread } }
FizzBuzz Thread:
for (int i = 15; i <= n; i = i + 15) { // Only check multiples of 15 fbSema.acquire(); // Wait for signal printFizzBuzz.run(); // Print "fizzbuzz" nSema.release(); // Return control to number thread }
Execution Flow Example (n = 5)
i = 1
: number thread prints 1, releasesnSema
i = 2
: number thread prints 2, releasesnSema
i = 3
: number thread releasesfSema
→ fizz thread prints "fizz", releasesnSema
i = 4
: number thread prints 4, releasesnSema
i = 5
: number thread releasesbSema
→ buzz thread prints "buzz", releasesnSema
Key Patterns Used
- Producer-Consumer Pattern: The number thread produces signals for other threads to consume
- Token Passing: The
nSema
acts like a token that gets passed between threads - Optimization: Each worker thread only iterates through relevant numbers (multiples of 3, 5, or 15) rather than checking all numbers, reducing unnecessary iterations
The beauty of this solution is that it guarantees mutual exclusion (only one thread prints at a time) and maintains the correct order without any explicit locks or complex synchronization logic.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a small example with n = 6
to illustrate how the threads coordinate:
Initial State:
nSema = 1
(number thread can start)fSema = 0
(fizz thread blocked)bSema = 0
(buzz thread blocked)fbSema = 0
(fizzbuzz thread blocked)
Thread Starting Positions:
- Number thread: ready to process
i = 1
- Fizz thread: waiting at
i = 3
(first multiple of 3) - Buzz thread: waiting at
i = 5
(first multiple of 5) - FizzBuzz thread: waiting at
i = 15
(no iterations for n=6)
Step-by-step Execution:
Step 1: i = 1
- Number thread acquires
nSema
(nownSema = 0
) - 1 is not divisible by 3 or 5
- Number thread prints "1"
- Number thread releases
nSema
(nownSema = 1
)
Step 2: i = 2
- Number thread acquires
nSema
(nownSema = 0
) - 2 is not divisible by 3 or 5
- Number thread prints "2"
- Number thread releases
nSema
(nownSema = 1
)
Step 3: i = 3
- Number thread acquires
nSema
(nownSema = 0
) - 3 is divisible by 3 but not 5
- Number thread releases
fSema
(nowfSema = 1
) - Fizz thread (waiting at i=3) acquires
fSema
(nowfSema = 0
) - Fizz thread prints "fizz"
- Fizz thread releases
nSema
(nownSema = 1
) - Fizz thread moves to next iteration (i=6)
Step 4: i = 4
- Number thread acquires
nSema
(nownSema = 0
) - 4 is not divisible by 3 or 5
- Number thread prints "4"
- Number thread releases
nSema
(nownSema = 1
)
Step 5: i = 5
- Number thread acquires
nSema
(nownSema = 0
) - 5 is divisible by 5 but not 3
- Number thread releases
bSema
(nowbSema = 1
) - Buzz thread (waiting at i=5) acquires
bSema
(nowbSema = 0
) - Buzz thread prints "buzz"
- Buzz thread releases
nSema
(nownSema = 1
) - Buzz thread moves to next iteration (i=10, exits loop since 10 > 6)
Step 6: i = 6
- Number thread acquires
nSema
(nownSema = 0
) - 6 is divisible by 3 but not 5
- Number thread releases
fSema
(nowfSema = 1
) - Fizz thread (waiting at i=6) acquires
fSema
(nowfSema = 0
) - Fizz thread prints "fizz"
- Fizz thread releases
nSema
(nownSema = 1
) - Fizz thread moves to next iteration (i=9, exits loop since 9 > 6)
Final Output: [1, 2, "fizz", 4, "buzz", "fizz"]
The key insight is that the number thread acts as a dispatcher, examining each number and signaling the appropriate worker thread. The semaphores ensure only one thread executes at a time, maintaining the correct sequential order.
Solution Implementation
1import threading
2
3class FizzBuzz:
4 def __init__(self, n: int):
5 """
6 Initialize FizzBuzz with upper limit n.
7
8 Args:
9 n: Upper limit of the sequence (inclusive)
10 """
11 self.n = n # Upper limit of the sequence
12 self.index = 1 # Current number being processed
13 self.lock = threading.Lock() # Mutex for thread synchronization
14
15 def fizz(self, printFizz: 'Callable[[], None]') -> None:
16 """
17 Print "Fizz" for numbers divisible by 3 but not 5.
18
19 Args:
20 printFizz: Callback function to print "Fizz"
21 """
22 while self.index <= self.n:
23 # Acquire lock to ensure thread-safe access to shared index
24 with self.lock:
25 # Check if current index is divisible by 3 but not 5
26 # Additional index <= n check ensures we don't exceed limit after acquiring lock
27 if self.index % 3 == 0 and self.index % 5 != 0 and self.index <= self.n:
28 printFizz()
29 self.index += 1
30
31 def buzz(self, printBuzz: 'Callable[[], None]') -> None:
32 """
33 Print "Buzz" for numbers divisible by 5 but not 3.
34
35 Args:
36 printBuzz: Callback function to print "Buzz"
37 """
38 while self.index <= self.n:
39 # Acquire lock to ensure thread-safe access to shared index
40 with self.lock:
41 # Check if current index is divisible by 5 but not 3
42 if self.index % 5 == 0 and self.index % 3 != 0 and self.index <= self.n:
43 printBuzz()
44 self.index += 1
45
46 def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:
47 """
48 Print "FizzBuzz" for numbers divisible by both 3 and 5 (i.e., divisible by 15).
49
50 Args:
51 printFizzBuzz: Callback function to print "FizzBuzz"
52 """
53 while self.index <= self.n:
54 # Acquire lock to ensure thread-safe access to shared index
55 with self.lock:
56 # Check if current index is divisible by 15 (both 3 and 5)
57 if self.index % 15 == 0 and self.index <= self.n:
58 printFizzBuzz()
59 self.index += 1
60
61 def number(self, printNumber: 'Callable[[int], None]') -> None:
62 """
63 Print the actual number for numbers not divisible by 3 or 5.
64
65 Args:
66 printNumber: Callback function to print the number
67 """
68 while self.index <= self.n:
69 # Acquire lock to ensure thread-safe access to shared index
70 with self.lock:
71 # Check if current index is not divisible by either 3 or 5
72 if self.index % 3 != 0 and self.index % 5 != 0 and self.index <= self.n:
73 printNumber(self.index)
74 self.index += 1
75
1class FizzBuzz {
2 // Maximum number to process
3 private int n;
4
5 // Semaphores for thread synchronization
6 private Semaphore fizzSemaphore = new Semaphore(0); // Controls fizz thread execution
7 private Semaphore buzzSemaphore = new Semaphore(0); // Controls buzz thread execution
8 private Semaphore fizzbuzzSemaphore = new Semaphore(0); // Controls fizzbuzz thread execution
9 private Semaphore numberSemaphore = new Semaphore(1); // Controls number thread execution (starts with 1 permit)
10
11 /**
12 * Constructor to initialize FizzBuzz with maximum number
13 * @param n the maximum number to process
14 */
15 public FizzBuzz(int n) {
16 this.n = n;
17 }
18
19 /**
20 * Prints "fizz" for numbers divisible by 3 but not by 5
21 * @param printFizz runnable that outputs "fizz"
22 */
23 public void fizz(Runnable printFizz) throws InterruptedException {
24 // Iterate through all numbers divisible by 3
25 for (int i = 3; i <= n; i += 3) {
26 // Only process if not divisible by 5 (not a fizzbuzz case)
27 if (i % 5 != 0) {
28 // Wait for permission from number thread
29 fizzSemaphore.acquire();
30 // Print "fizz"
31 printFizz.run();
32 // Signal number thread to continue
33 numberSemaphore.release();
34 }
35 }
36 }
37
38 /**
39 * Prints "buzz" for numbers divisible by 5 but not by 3
40 * @param printBuzz runnable that outputs "buzz"
41 */
42 public void buzz(Runnable printBuzz) throws InterruptedException {
43 // Iterate through all numbers divisible by 5
44 for (int i = 5; i <= n; i += 5) {
45 // Only process if not divisible by 3 (not a fizzbuzz case)
46 if (i % 3 != 0) {
47 // Wait for permission from number thread
48 buzzSemaphore.acquire();
49 // Print "buzz"
50 printBuzz.run();
51 // Signal number thread to continue
52 numberSemaphore.release();
53 }
54 }
55 }
56
57 /**
58 * Prints "fizzbuzz" for numbers divisible by both 3 and 5
59 * @param printFizzBuzz runnable that outputs "fizzbuzz"
60 */
61 public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException {
62 // Iterate through all numbers divisible by 15 (both 3 and 5)
63 for (int i = 15; i <= n; i += 15) {
64 // Wait for permission from number thread
65 fizzbuzzSemaphore.acquire();
66 // Print "fizzbuzz"
67 printFizzBuzz.run();
68 // Signal number thread to continue
69 numberSemaphore.release();
70 }
71 }
72
73 /**
74 * Controls the main flow and prints numbers not divisible by 3 or 5
75 * @param printNumber consumer that outputs the number
76 */
77 public void number(IntConsumer printNumber) throws InterruptedException {
78 // Process each number from 1 to n
79 for (int i = 1; i <= n; i++) {
80 // Acquire permit to process current number
81 numberSemaphore.acquire();
82
83 // Determine which thread should handle this number
84 if (i % 3 == 0 && i % 5 == 0) {
85 // Number divisible by both 3 and 5: delegate to fizzbuzz thread
86 fizzbuzzSemaphore.release();
87 } else if (i % 3 == 0) {
88 // Number divisible by 3 only: delegate to fizz thread
89 fizzSemaphore.release();
90 } else if (i % 5 == 0) {
91 // Number divisible by 5 only: delegate to buzz thread
92 buzzSemaphore.release();
93 } else {
94 // Number not divisible by 3 or 5: print the number itself
95 printNumber.accept(i);
96 // Continue to next iteration
97 numberSemaphore.release();
98 }
99 }
100 }
101}
102
1class FizzBuzz {
2private:
3 std::mutex mtx_; // Mutex for thread synchronization
4 std::atomic<int> index_; // Current number being processed (atomic for thread-safe access)
5 int n_; // Upper limit of the sequence
6
7public:
8 FizzBuzz(int n) : n_(n), index_(1) {
9 // Initialize with n as the upper bound and index starting at 1
10 }
11
12 // Print "Fizz" for numbers divisible by 3 but not 5
13 void fizz(std::function<void()> printFizz) {
14 while (index_ <= n_) {
15 // RAII lock - automatically releases when going out of scope
16 std::lock_guard<std::mutex> lock(mtx_);
17
18 // Check if current index is divisible by 3 but not 5
19 // Additional index <= n check ensures we don't exceed limit after acquiring lock
20 if (index_ % 3 == 0 && index_ % 5 != 0 && index_ <= n_) {
21 printFizz();
22 index_++;
23 }
24 }
25 }
26
27 // Print "Buzz" for numbers divisible by 5 but not 3
28 void buzz(std::function<void()> printBuzz) {
29 while (index_ <= n_) {
30 std::lock_guard<std::mutex> lock(mtx_);
31
32 // Check if current index is divisible by 5 but not 3
33 if (index_ % 5 == 0 && index_ % 3 != 0 && index_ <= n_) {
34 printBuzz();
35 index_++;
36 }
37 }
38 }
39
40 // Print "FizzBuzz" for numbers divisible by both 3 and 5 (i.e., divisible by 15)
41 void fizzbuzz(std::function<void()> printFizzBuzz) {
42 while (index_ <= n_) {
43 std::lock_guard<std::mutex> lock(mtx_);
44
45 // Check if current index is divisible by 15 (both 3 and 5)
46 if (index_ % 15 == 0 && index_ <= n_) {
47 printFizzBuzz();
48 index_++;
49 }
50 }
51 }
52
53 // Print the actual number for numbers not divisible by 3 or 5
54 void number(std::function<void(int)> printNumber) {
55 while (index_ <= n_) {
56 std::lock_guard<std::mutex> lock(mtx_);
57
58 // Check if current index is not divisible by either 3 or 5
59 if (index_ % 3 != 0 && index_ % 5 != 0 && index_ <= n_) {
60 printNumber(index_);
61 index_++;
62 }
63 }
64 }
65};
66
1// Mutex for thread synchronization (simulated using a simple lock mechanism)
2let mtx: boolean = false;
3// Current number being processed
4let index: number = 1;
5// Upper limit of the sequence
6let n: number = 0;
7
8/**
9 * Initialize the FizzBuzz sequence with upper bound n
10 * @param limit - The upper limit of the sequence
11 */
12function initFizzBuzz(limit: number): void {
13 n = limit;
14 index = 1;
15 mtx = false;
16}
17
18/**
19 * Acquire lock for thread synchronization
20 * In TypeScript/JavaScript, this is simplified since it's single-threaded
21 */
22function acquireLock(): void {
23 while (mtx) {
24 // Busy wait (in real scenario, would use proper async mechanisms)
25 }
26 mtx = true;
27}
28
29/**
30 * Release lock for thread synchronization
31 */
32function releaseLock(): void {
33 mtx = false;
34}
35
36/**
37 * Print "Fizz" for numbers divisible by 3 but not 5
38 * @param printFizz - Callback function to print "Fizz"
39 */
40function fizz(printFizz: () => void): void {
41 while (index <= n) {
42 acquireLock();
43
44 // Check if current index is divisible by 3 but not 5
45 // Additional index <= n check ensures we don't exceed limit after acquiring lock
46 if (index % 3 === 0 && index % 5 !== 0 && index <= n) {
47 printFizz();
48 index++;
49 }
50
51 releaseLock();
52 }
53}
54
55/**
56 * Print "Buzz" for numbers divisible by 5 but not 3
57 * @param printBuzz - Callback function to print "Buzz"
58 */
59function buzz(printBuzz: () => void): void {
60 while (index <= n) {
61 acquireLock();
62
63 // Check if current index is divisible by 5 but not 3
64 if (index % 5 === 0 && index % 3 !== 0 && index <= n) {
65 printBuzz();
66 index++;
67 }
68
69 releaseLock();
70 }
71}
72
73/**
74 * Print "FizzBuzz" for numbers divisible by both 3 and 5 (i.e., divisible by 15)
75 * @param printFizzBuzz - Callback function to print "FizzBuzz"
76 */
77function fizzbuzz(printFizzBuzz: () => void): void {
78 while (index <= n) {
79 acquireLock();
80
81 // Check if current index is divisible by 15 (both 3 and 5)
82 if (index % 15 === 0 && index <= n) {
83 printFizzBuzz();
84 index++;
85 }
86
87 releaseLock();
88 }
89}
90
91/**
92 * Print the actual number for numbers not divisible by 3 or 5
93 * @param printNumber - Callback function to print the number
94 */
95function number(printNumber: (num: number) => void): void {
96 while (index <= n) {
97 acquireLock();
98
99 // Check if current index is not divisible by either 3 or 5
100 if (index % 3 !== 0 && index % 5 !== 0 && index <= n) {
101 printNumber(index);
102 index++;
103 }
104
105 releaseLock();
106 }
107}
108
Time and Space Complexity
Time Complexity: O(n)
The time complexity is determined by analyzing each method's execution:
- The
number
method iterates from 1 to n, executing exactlyn
iterations:O(n)
- The
fizz
method iterates through multiples of 3 up to n, executing approximatelyn/3
iterations, but only processes numbers wherei % 5 != 0
, which is approximatelyn/3 * 4/5 = 4n/15
iterations:O(n)
- The
buzz
method iterates through multiples of 5 up to n, executing approximatelyn/5
iterations, but only processes numbers wherei % 3 != 0
, which is approximatelyn/5 * 2/3 = 2n/15
iterations:O(n)
- The
fizzbuzz
method iterates through multiples of 15 up to n, executing approximatelyn/15
iterations:O(n)
Since these methods run concurrently in separate threads and coordinate through semaphores, the overall time complexity remains O(n)
as the number
method drives the progression through all values from 1 to n.
Space Complexity: O(1)
The space complexity analysis:
- The class uses 4 semaphore objects (
fSema
,bSema
,fbSema
,nSema
), each requiring constant space:O(1)
- One integer instance variable
n
is stored:O(1)
- Each method uses a constant amount of local variables (loop counter
i
):O(1)
- No additional data structures that grow with input size are used
The total space complexity is O(1)
as the memory usage remains constant regardless of the value of n.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Busy Waiting and CPU Waste
The provided Python solution suffers from a severe performance issue: all four threads are continuously spinning in tight while
loops, constantly acquiring and releasing the lock even when it's not their turn to print. This leads to:
- High CPU usage: Threads are constantly checking conditions that will fail most of the time
- Lock contention: Threads repeatedly compete for the lock unnecessarily
- Poor scalability: Performance degrades significantly as n increases
Example of the problem: When processing number 7, all four threads will:
- Acquire the lock
- Check their condition (3 threads will fail)
- Release the lock
- Immediately try again
This happens thousands of times for each number!
2. Improved Solution Using Condition Variables
Here's a better approach that eliminates busy waiting:
import threading
class FizzBuzz:
def __init__(self, n: int):
self.n = n
self.current = 1
self.lock = threading.Lock()
# Use a condition variable to avoid busy waiting
self.cv = threading.Condition(self.lock)
def fizz(self, printFizz: 'Callable[[], None]') -> None:
for i in range(3, self.n + 1, 3): # Only iterate through multiples of 3
if i % 5 != 0: # Skip multiples of 15
with self.cv:
# Wait until it's our turn (current reaches i)
while self.current != i:
self.cv.wait()
printFizz()
self.current += 1
self.cv.notify_all() # Wake up all waiting threads
def buzz(self, printBuzz: 'Callable[[], None]') -> None:
for i in range(5, self.n + 1, 5): # Only iterate through multiples of 5
if i % 3 != 0: # Skip multiples of 15
with self.cv:
while self.current != i:
self.cv.wait()
printBuzz()
self.current += 1
self.cv.notify_all()
def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:
for i in range(15, self.n + 1, 15): # Only iterate through multiples of 15
with self.cv:
while self.current != i:
self.cv.wait()
printFizzBuzz()
self.current += 1
self.cv.notify_all()
def number(self, printNumber: 'Callable[[int], None]') -> None:
for i in range(1, self.n + 1):
if i % 3 != 0 and i % 5 != 0: # Only handle non-fizzbuzz numbers
with self.cv:
while self.current != i:
self.cv.wait()
printNumber(i)
self.current += 1
self.cv.notify_all()
3. Alternative Solution Using Semaphores (Matching the Java Approach)
For consistency with the Java solution described earlier:
import threading
class FizzBuzz:
def __init__(self, n: int):
self.n = n
self.nSema = threading.Semaphore(1) # Number thread starts
self.fSema = threading.Semaphore(0) # Fizz thread blocked
self.bSema = threading.Semaphore(0) # Buzz thread blocked
self.fbSema = threading.Semaphore(0) # FizzBuzz thread blocked
def fizz(self, printFizz: 'Callable[[], None]') -> None:
for i in range(3, self.n + 1, 3):
if i % 5 != 0:
self.fSema.acquire()
printFizz()
self.nSema.release()
def buzz(self, printBuzz: 'Callable[[], None]') -> None:
for i in range(5, self.n + 1, 5):
if i % 3 != 0:
self.bSema.acquire()
printBuzz()
self.nSema.release()
def fizzbuzz(self, printFizzBuzz: 'Callable[[], None]') -> None:
for i in range(15, self.n + 1, 15):
self.fbSema.acquire()
printFizzBuzz()
self.nSema.release()
def number(self, printNumber: 'Callable[[int], None]') -> None:
for i in range(1, self.n + 1):
self.nSema.acquire()
if i % 15 == 0:
self.fbSema.release()
elif i % 3 == 0:
self.fSema.release()
elif i % 5 == 0:
self.bSema.release()
else:
printNumber(i)
self.nSema.release()
Key Improvements:
- No busy waiting: Threads sleep when it's not their turn
- Predictable iteration: Each thread knows exactly which numbers it handles
- Better CPU utilization: No wasted cycles checking conditions repeatedly
- Cleaner logic: Each thread has a clear responsibility
Which of the following uses divide and conquer strategy?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!