Mastering Java Multithreading Interview Challenges
Written on
Chapter 1: Understanding the Scenario
Consider a situation where you have two threads: Thread1 and Thread2. In Thread1, an integer variable named "count" is incremented by 1. Meanwhile, Thread2 checks if the count exceeds 1 and, if so, prints the message "t2 running."
When we execute the run() method 100 times and print the current iteration number, the output appears as follows:
In certain iterations (for instance, 91 and 97), the output from Thread2 fails to appear. This inconsistency arises because there's no guarantee that Thread1 will complete its execution before Thread2 starts.
Now, the challenge is to modify the code so that the message from Thread2 is consistently printed. Here are some guidelines: Avoid using Thread.sleep() or similar methods as a quick fix. You are not allowed to alter the existing logic for Thread1 and Thread2, but you may add code before or after the existing logic.
Let’s take a moment to think about potential solutions. Perhaps you’re considering this approach?
Some might suggest implementing a locking mechanism using an object. By utilizing the wait-notify paradigm, we can have Thread2 wait initially. Once Thread1 completes its increment, it can notify Thread2 to proceed.
However, during testing, I found that out of 100 executions, only 5 times did we get the "t2 running" output. What could explain this?
The issue arises because Thread1 often calls notify() before Thread2 has a chance to enter the wait state. As a result, when Thread1 sends the notification, no thread is waiting, which leads Thread2 to enter an indefinite wait state. How can we resolve this?
To fix the problem, we must ensure that Thread2 is waiting before Thread1 sends the notification. One effective method is to utilize FutureTask. We can create a customized FutureTask that, when called, returns a string "MyTask."
In Thread1, we instruct the FutureTask object to fetch the result. In Thread2, we execute the task. This ensures that Thread1 only increments the count after Thread2 has executed the task and is waiting in the synchronized block. After updating the count, Thread1 then notifies Thread2 to proceed with printing the message.
Let’s run the program 100 more times to check if every execution returns the "t2 running" message.
As demonstrated, by employing FutureTask and a synchronized lock, all 100 iterations successfully printed the message from Thread2.
Of course, there are alternative methods to tackle this issue, such as using a blocking queue or a semaphore. Feel free to share your solutions in the comments.
I hope this article proves to be beneficial. If you’re eager to delve deeper into Java and backend development, consider following my channel for insights drawn from my professional experiences and daily life.
Chapter 2: Video Resources
Explore solutions for all Leetcode multithreading questions in this video, providing a comprehensive guide to tackling challenges in Java.
Watch this complete two-hour course on Java concurrency and multithreading, taking you from beginner to expert level.